MOBILE-3631 messages: Add discussion page
parent
b977dcf4ca
commit
18cb43aa5e
|
@ -20,6 +20,7 @@ import { CoreCourses } from '@features/courses/services/courses';
|
|||
import { CoreSite, CoreSiteWSPreSets } from '@classes/site';
|
||||
import { CoreStatusWithWarningsWSResponse, CoreWSExternalWarning } from '@services/ws';
|
||||
import { makeSingleton } from '@singletons';
|
||||
import { CoreError } from '@classes/errors/error';
|
||||
|
||||
const ROOT_CACHE_KEY = 'mmaCourseCompletion:';
|
||||
|
||||
|
@ -118,7 +119,7 @@ export class AddonCourseCompletionProvider {
|
|||
return result.completionstatus;
|
||||
}
|
||||
|
||||
throw null;
|
||||
throw new CoreError('Cannot fetch course completion status');
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -165,7 +166,7 @@ export class AddonCourseCompletionProvider {
|
|||
*/
|
||||
async isPluginViewEnabledForCourse(courseId: number, preferCache: boolean = true): Promise<boolean> {
|
||||
if (!courseId) {
|
||||
throw null;
|
||||
throw new CoreError('No courseId provided');
|
||||
}
|
||||
|
||||
const course = await CoreCourses.instance.getUserCourse(courseId, preferCache);
|
||||
|
@ -260,7 +261,7 @@ export class AddonCourseCompletionProvider {
|
|||
const response = await site.write<CoreStatusWithWarningsWSResponse>('core_completion_mark_course_self_completed', params);
|
||||
|
||||
if (!response.status) {
|
||||
throw null;
|
||||
throw new CoreError('Cannot mark course as self completed');
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -31,6 +31,11 @@ function buildRoutes(injector: Injector): Routes {
|
|||
loadChildren: () => import('./pages/group-conversations/group-conversations.module')
|
||||
.then(m => m.AddonMessagesGroupConversationsPageModule),
|
||||
},
|
||||
{
|
||||
path: 'discussion',
|
||||
loadChildren: () => import('./pages/discussion/discussion.module')
|
||||
.then(m => m.AddonMessagesDiscussionPageModule),
|
||||
},
|
||||
{
|
||||
path: 'search',
|
||||
loadChildren: () => import('./pages/search/search.module')
|
||||
|
|
|
@ -0,0 +1,150 @@
|
|||
<ion-header>
|
||||
<ion-toolbar>
|
||||
<ion-buttons slot="start">
|
||||
<ion-back-button [attr.aria-label]="'core.back' | translate"></ion-back-button>
|
||||
</ion-buttons>
|
||||
<ion-title>
|
||||
<div class="toolbar-title">
|
||||
<img *ngIf="loaded && !otherMember && conversationImage" class="core-bar-button-image" [src]="conversationImage"
|
||||
[alt]="title" onError="this.src='assets/img/group-avatar.png'" core-external-content role="presentation"
|
||||
[siteId]="siteId || null">
|
||||
<core-user-avatar *ngIf="loaded && otherMember" class="core-bar-button-image" [user]="otherMember"
|
||||
[linkProfile]="false" [checkOnline]="otherMember.showonlinestatus" (click)="showInfo && viewInfo()">
|
||||
</core-user-avatar>
|
||||
<core-format-text [text]="title" contextLevel="system" [contextInstanceId]="0"
|
||||
(click)="showInfo && !isGroup && viewInfo()"></core-format-text>
|
||||
<ion-icon *ngIf="conversation && conversation.isfavourite" name="fas-star" [title]="'core.favourites' | translate">
|
||||
</ion-icon>
|
||||
<ion-icon *ngIf="conversation && conversation.ismuted" name="fas-bell-slash"
|
||||
[title]="'addon.messages.mutedconversation' | translate">
|
||||
</ion-icon>
|
||||
</div>
|
||||
</ion-title>
|
||||
<ion-buttons slot="end"></ion-buttons>
|
||||
</ion-toolbar>
|
||||
<core-navbar-buttons slot="end">
|
||||
<core-context-menu [aria-label]="'addon.messages.conversationactions' | translate">
|
||||
<core-context-menu-item [hidden]="isSelf || !showInfo || isGroup" [priority]="1000"
|
||||
[content]="'addon.messages.info' | translate" (action)="viewInfo()"
|
||||
iconAction="fas-info-circle"></core-context-menu-item>
|
||||
<core-context-menu-item [hidden]="isSelf || !showInfo || !isGroup" [priority]="1000"
|
||||
[content]="'addon.messages.groupinfo' | translate" (action)="viewInfo()"
|
||||
iconAction="fas-info-circle"></core-context-menu-item>
|
||||
<core-context-menu-item [hidden]="!groupMessagingEnabled || !conversation" [priority]="800"
|
||||
[content]="(conversation && conversation.isfavourite ? 'addon.messages.removefromfavourites' :
|
||||
'addon.messages.addtofavourites') | translate"
|
||||
(action)="changeFavourite($event)" [closeOnClick]="false" [iconAction]="favouriteIcon"
|
||||
[iconSlash]="favouriteIconSlash"></core-context-menu-item>
|
||||
<core-context-menu-item [hidden]="isSelf || !otherMember || otherMember.isblocked" [priority]="700"
|
||||
[content]="'addon.messages.blockuser' | translate" (action)="blockUser()" [iconAction]="blockIcon">
|
||||
</core-context-menu-item>
|
||||
<core-context-menu-item [hidden]="isSelf || !otherMember || !otherMember.isblocked" [priority]="700"
|
||||
[content]="'addon.messages.unblockuser' | translate" (action)="unblockUser()" [iconAction]="blockIcon">
|
||||
</core-context-menu-item>
|
||||
<core-context-menu-item [hidden]="isSelf || !muteEnabled || !conversation" [priority]="600"
|
||||
[content]="(conversation && conversation.ismuted ? 'addon.messages.unmuteconversation' :
|
||||
'addon.messages.muteconversation') | translate" (action)="changeMute($event)" [closeOnClick]="false"
|
||||
[iconAction]="muteIcon"></core-context-menu-item>
|
||||
<core-context-menu-item [hidden]="!canDelete || !messages || !messages.length" [priority]="400"
|
||||
[content]="'addon.messages.showdeletemessages' | translate" (action)="toggleDelete()"
|
||||
[iconAction]="(showDelete ? 'far-check-square' : 'far-square')"></core-context-menu-item>
|
||||
<core-context-menu-item [hidden]="!groupMessagingEnabled || !conversationId || isGroup || !messages || !messages.length"
|
||||
[priority]="200" [content]="'addon.messages.deleteconversation' | translate" (action)="deleteConversation($event)"
|
||||
[closeOnClick]="false" [iconAction]="deleteIcon"></core-context-menu-item>
|
||||
<core-context-menu-item
|
||||
[hidden]="isSelf || !otherMember || otherMember.iscontact || requestContactSent || requestContactReceived"
|
||||
[priority]="100" [content]="'addon.messages.addtoyourcontacts' | translate" (action)="createContactRequest()"
|
||||
[iconAction]="addRemoveIcon"></core-context-menu-item>
|
||||
<core-context-menu-item [hidden]="isSelf || !otherMember || !otherMember.iscontact" [priority]="100"
|
||||
[content]="'addon.messages.removefromyourcontacts' | translate" (action)="removeContact()"
|
||||
[iconAction]="addRemoveIcon" [iconSlash]="true"></core-context-menu-item>
|
||||
</core-context-menu>
|
||||
</core-navbar-buttons>
|
||||
</ion-header>
|
||||
<ion-content class="has-footer" (ionScroll)="scrollFunction($event)">
|
||||
<core-loading [hideUntil]="loaded" class="safe-area-page">
|
||||
<!-- Load previous messages. -->
|
||||
<core-infinite-loading [enabled]="canLoadMore" (action)="loadPrevious($event)" position="top" [error]="loadMoreError">
|
||||
</core-infinite-loading>
|
||||
|
||||
<ng-container *ngIf="isSelf && !canLoadMore">
|
||||
<p class="ion-text-center">{{ 'addon.messages.selfconversation' | translate }}</p>
|
||||
<p class="ion-text-center"><i>{{ 'addon.messages.selfconversationdefaultmessage' | translate }}</i></p>
|
||||
</ng-container>
|
||||
|
||||
<ion-list class="addon-messages-discussion-container" [class.addon-messages-discussion-group]="isGroup" [attr.aria-live]="'polite'">
|
||||
<ng-container *ngFor="let message of messages; index as index; last as last">
|
||||
<h6 class="ion-text-center addon-messages-date" *ngIf="message.showDate">
|
||||
{{ message.timecreated | coreFormatDate: "strftimedayshort" }}
|
||||
</h6>
|
||||
|
||||
<ion-chip class="addon-messages-unreadfrom" *ngIf="unreadMessageFrom && message.id == unreadMessageFrom" color="light">
|
||||
<ion-label>{{ 'addon.messages.newmessages' | translate }}</ion-label>
|
||||
<ion-icon name="arrow-round-down"></ion-icon>
|
||||
</ion-chip>
|
||||
|
||||
<ion-item class="ion-text-wrap addon-message" (longPress)="copyMessage(message)" [class.addon-message-mine]="message.useridfrom == currentUserId" [class.addon-message-not-mine]="message.useridfrom != currentUserId" [class.addon-message-no-user]="!message.showUserData" [@coreSlideInOut]="message.useridfrom == currentUserId ? '' : 'fromLeft'">
|
||||
<ion-label>
|
||||
<!-- User data. -->
|
||||
<h2 class="addon-message-user">
|
||||
<core-user-avatar slot="start" [user]="members[message.useridfrom]" [linkProfile]="false"
|
||||
*ngIf="message.showUserData"></core-user-avatar>
|
||||
|
||||
<div *ngIf="message.showUserData">{{ members[message.useridfrom].fullname }}</div>
|
||||
|
||||
<ion-note *ngIf="!message.pending">{{ message.timecreated | coreFormatDate: "strftimetime" }}</ion-note>
|
||||
<ion-note *ngIf="message.pending"><ion-icon name="far-clock"></ion-icon></ion-note>
|
||||
</h2>
|
||||
|
||||
<!-- Some messages have <p> and some others don't. Add a <p> so they all have same styles. -->
|
||||
<p class="addon-message-text">
|
||||
<core-format-text (afterRender)="last && scrollToBottom()" [text]="message.text" contextLevel="system" [contextInstanceId]="0"></core-format-text>
|
||||
</p>
|
||||
</ion-label>
|
||||
<ion-button fill="clear" *ngIf="!message.sending && showDelete" (click)="deleteMessage(message, index)"
|
||||
class="addon-messages-delete-button" [@coreSlideInOut]="'fromRight'"
|
||||
[attr.aria-label]=" 'addon.messages.deletemessage' | translate" slot="end">
|
||||
<ion-icon name="fas-trash" color="danger" slot="icon-only"></ion-icon>
|
||||
</ion-button>
|
||||
|
||||
<div class="tail" *ngIf="message.showTail"></div>
|
||||
</ion-item>
|
||||
</ng-container>
|
||||
</ion-list>
|
||||
|
||||
<core-empty-box *ngIf="!messages || messages.length <= 0" icon="far-comments" [message]="'addon.messages.nomessagesfound' | translate"></core-empty-box>
|
||||
</core-loading>
|
||||
<!-- Scroll bottom. -->
|
||||
<ion-fab slot="fixed" core-fab vertical="bottom" horizontal="end" *ngIf="loaded && newMessages > 0">
|
||||
<ion-fab-button size="small" (click)="scrollToFirstUnreadMessage(true)" color="light"
|
||||
[attr.aria-label]="'addon.messages.newmessages' | translate">
|
||||
<ion-icon name="fas-arrow-down"></ion-icon>
|
||||
<span class="core-discussion-messages-badge">{{ newMessages }}</span>
|
||||
</ion-fab-button>
|
||||
</ion-fab>
|
||||
</ion-content>
|
||||
<ion-footer color="light" class="footer-adjustable" *ngIf="loaded && (!conversationId || conversation)">
|
||||
<ion-toolbar color="light">
|
||||
<p *ngIf="footerType == 'unable'" class="ion-text-center ion-margin-horizontal">{{ 'addon.messages.unabletomessage' | translate }}</p>
|
||||
<div *ngIf="footerType == 'blocked'" class="ion-padding-horizontal">
|
||||
<p class="ion-text-center">{{ 'addon.messages.youhaveblockeduser' | translate }}</p>
|
||||
<ion-button expand="block" class="ion-text-wrap ion-margin-bottom" (click)="unblockUser()">{{ 'addon.messages.unblockuser' | translate }}</ion-button>
|
||||
</div>
|
||||
<div *ngIf="footerType == 'requiresContact'" class="ion-padding-horizontal">
|
||||
<p class="ion-text-center"><strong>{{ 'addon.messages.isnotinyourcontacts' | translate: {$a: otherMember.fullname} }}</strong></p>
|
||||
<p class="ion-text-center">{{ 'addon.messages.requirecontacttomessage' | translate: {$a: otherMember.fullname} }}</p>
|
||||
<ion-button expand="block" class="ion-text-wrap ion-margin-bottom" (click)="createContactRequest()">{{ 'addon.messages.sendcontactrequest' | translate }}</ion-button>
|
||||
</div>
|
||||
<div *ngIf="footerType == 'requestReceived'" class="ion-padding-horizontal">
|
||||
<p class="ion-text-center">{{ 'addon.messages.userwouldliketocontactyou' | translate: {$a: otherMember.fullname} }}</p>
|
||||
<ion-button expand="block" class="ion-text-wrap ion-margin-bottom" (click)="confirmContactRequest()">{{ 'addon.messages.acceptandaddcontact' | translate }}</ion-button>
|
||||
<ion-button expand="block" class="ion-text-wrap ion-margin-bottom" color="light" (click)="declineContactRequest()">{{ 'addon.messages.decline' | translate }}</ion-button>
|
||||
</div>
|
||||
<div *ngIf="footerType == 'requestSent' || (footerType == 'message' && requestContactSent)" class="ion-padding-horizontal">
|
||||
<p class="ion-text-center"><strong>{{ 'addon.messages.contactrequestsent' | translate }}</strong></p>
|
||||
<p class="ion-text-center">{{ 'addon.messages.yourcontactrequestpending' | translate: {$a: otherMember.fullname} }}</p>
|
||||
</div>
|
||||
<core-send-message-form *ngIf="footerType == 'message'" (onSubmit)="sendMessage($event)" [showKeyboard]="showKeyboard"
|
||||
[placeholder]="'addon.messages.newmessage' | translate" (onResize)="resizeContent()"></core-send-message-form>
|
||||
</ion-toolbar>
|
||||
</ion-footer>
|
|
@ -0,0 +1,46 @@
|
|||
// (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 { NgModule } from '@angular/core';
|
||||
import { IonicModule } from '@ionic/angular';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
import { RouterModule, Routes } from '@angular/router';
|
||||
import { CommonModule } from '@angular/common';
|
||||
|
||||
import { CoreSharedModule } from '@/core/shared.module';
|
||||
|
||||
import { AddonMessagesDiscussionPage } from './discussion.page';
|
||||
|
||||
const routes: Routes = [
|
||||
{
|
||||
path: '',
|
||||
component: AddonMessagesDiscussionPage,
|
||||
},
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
RouterModule.forChild(routes),
|
||||
CommonModule,
|
||||
IonicModule,
|
||||
TranslateModule.forChild(),
|
||||
CoreSharedModule,
|
||||
],
|
||||
declarations: [
|
||||
AddonMessagesDiscussionPage,
|
||||
],
|
||||
exports: [RouterModule],
|
||||
})
|
||||
export class AddonMessagesDiscussionPageModule {}
|
||||
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,300 @@
|
|||
:host {
|
||||
ion-content {
|
||||
background-color: var(--background-lighter);
|
||||
|
||||
&::part(scroll) {
|
||||
padding-bottom: 0 !important;
|
||||
}
|
||||
}
|
||||
|
||||
.addon-messages-discussion-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding-bottom: 15px;
|
||||
background: var(--background-lighter);
|
||||
}
|
||||
|
||||
.addon-messages-date {
|
||||
font-weight: normal;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.addon-messages-unreadfrom {
|
||||
color: var(--core-color);
|
||||
background-color: transparent;
|
||||
margin-top: 6px;
|
||||
ion-icon {
|
||||
color: var(--core-color);
|
||||
background-color: transparent;
|
||||
}
|
||||
}
|
||||
|
||||
// Message item.
|
||||
ion-item.addon-message {
|
||||
border: 0;
|
||||
border-radius: 4px;
|
||||
padding: 8px;
|
||||
margin: 8px 8px 0 8px;
|
||||
--background: var(--addon-messages-message-bg);
|
||||
background: var(--background);
|
||||
align-self: flex-start;
|
||||
width: 90%;
|
||||
max-width: 90%;
|
||||
min-height: 0;
|
||||
position: relative;
|
||||
-webkit-transition: width 500ms ease-in-out;
|
||||
transition: width 500ms ease-in-out;
|
||||
// This is needed to display bubble tails.
|
||||
overflow: visible;
|
||||
|
||||
&::part(native) {
|
||||
--inner-border-width: 0;
|
||||
--inner-padding-end: 0;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
core-format-text > p:only-child {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
.addon-message-user {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: .5rem;
|
||||
margin-top: 0;
|
||||
color: var(--ion-text-color);
|
||||
|
||||
core-user-avatar {
|
||||
display: block;
|
||||
--core-avatar-size: var(--addon-messages-avatar-size);
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
div {
|
||||
font-weight: 500;
|
||||
flex-grow: 1;
|
||||
padding-right: .5rem;
|
||||
padding-left: .5rem;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
ion-note {
|
||||
text-align: end;;
|
||||
color: var(--addon-messages-message-note-text);
|
||||
font-size: var(--addon-messages-message-note-font-size);
|
||||
}
|
||||
}
|
||||
|
||||
&.addon-message-no-user .addon-message-user ion-note {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
&:active {
|
||||
--background: var(--addon-messages-message-activated-bg);
|
||||
}
|
||||
|
||||
ion-label {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.addon-message-text {
|
||||
display: inline-flex;
|
||||
* {
|
||||
color: var(--ion-text-color);
|
||||
}
|
||||
}
|
||||
|
||||
.addon-messages-delete-button {
|
||||
min-height: initial;
|
||||
line-height: initial;
|
||||
margin: 0 0 0 10px;
|
||||
height: 1.6em !important;
|
||||
-webkit-align-self: flex-end;
|
||||
align-self: flex-end;
|
||||
vertical-align: middle;
|
||||
float: inline-end;
|
||||
|
||||
ion-icon {
|
||||
font-size: 1.4em;
|
||||
line-height: initial;
|
||||
color: var(--ion-color-danger);
|
||||
}
|
||||
}
|
||||
|
||||
.tail {
|
||||
content: '';
|
||||
width: 0;
|
||||
height: 0;
|
||||
border: 0.5rem solid transparent;
|
||||
position: absolute;
|
||||
touch-action: none;
|
||||
}
|
||||
|
||||
// Defines when an item-message is the user's.
|
||||
&.addon-message-mine {
|
||||
--background: var(--addon-messages-message-mine-bg);
|
||||
align-self: flex-end;
|
||||
|
||||
&:active {
|
||||
--background: var(--addon-messages-message-mine-activated-bg);
|
||||
}
|
||||
|
||||
.spinner {
|
||||
float: inline-end;
|
||||
margin: 2px, -3px, -2px, 5px;
|
||||
|
||||
svg {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
.tail {
|
||||
right: -8px;
|
||||
bottom: -8px;
|
||||
margin-right: -0.5rem;
|
||||
border-bottom-color: var(--addon-messages-message-mine-bg);
|
||||
}
|
||||
|
||||
&:active .tail {
|
||||
border-bottom-color: var(--addon-messages-message-mine-activated-bg);
|
||||
}
|
||||
}
|
||||
|
||||
&.addon-message-not-mine .tail {
|
||||
border-bottom-color: var(--addon-messages-message-bg);
|
||||
bottom: -8px;
|
||||
left: -8px;
|
||||
margin-left: -0.5rem;
|
||||
}
|
||||
|
||||
&.addon-message-not-mine.activated .tail {
|
||||
border-bottom-color: var(--addon-messages-message-activated-bg);
|
||||
}
|
||||
}
|
||||
|
||||
ion-item.addon-message.addon-message-mine + ion-item.addon-message.addon-message-no-user.addon-message-mine,
|
||||
ion-item.addon-message.addon-message-not-mine + ion-item.addon-message.addon-message-no-user.addon-message-not-mine {
|
||||
h2 {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
margin-top: -8px;
|
||||
padding-top: 0;
|
||||
border-top-right-radius: 0;
|
||||
border-top-left-radius: 0;
|
||||
}
|
||||
|
||||
.has-fab .scroll-content {
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
ion-fab ion-fab-button {
|
||||
&::part(native) {
|
||||
contain: unset;
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
.core-discussion-messages-badge {
|
||||
position: absolute;
|
||||
border-radius: 50%;
|
||||
color: var(--addon-messages-discussion-badge-text);
|
||||
background-color: var(--addon-messages-discussion-badge);
|
||||
display: block;
|
||||
line-height: 20px;
|
||||
height: 20px;
|
||||
width: 20px;
|
||||
right: -6px;
|
||||
top: -6px;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
ion-header ion-toolbar .toolbar-title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 0;
|
||||
|
||||
.core-bar-button-image {
|
||||
margin-right: 6px;
|
||||
}
|
||||
|
||||
core-format-text {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
flex-shrink: 1;
|
||||
}
|
||||
|
||||
ion-icon {
|
||||
margin-left: 6px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
:host-context([dir=rtl]) {
|
||||
ion-header ion-toolbar .toolbar-title {
|
||||
.core-bar-button-image {
|
||||
margin-left: 6px;
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
ion-icon {
|
||||
margin-right: 6px;
|
||||
margin-left: 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Message item.
|
||||
ion-item.addon-message {
|
||||
|
||||
.addon-messages-delete-button {
|
||||
margin-right: 10px;
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
&.addon-message-mine {
|
||||
.spinner {
|
||||
margin-right: 5px;
|
||||
margin-left: -3px;
|
||||
}
|
||||
|
||||
.tail {
|
||||
right: unset;
|
||||
left: -8px;
|
||||
margin-right: unset;
|
||||
margin-left: 0;
|
||||
}
|
||||
}
|
||||
|
||||
&.addon-message-not-mine .tail {
|
||||
right: -8px;
|
||||
margin-right: -0.5rem;
|
||||
left: unset;
|
||||
margin-left: 0;
|
||||
}
|
||||
}
|
||||
|
||||
ion-fab button {
|
||||
.core-discussion-messages-badge {
|
||||
left: -6px;
|
||||
right: unset;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
:host-context(.ios) {
|
||||
ion-header ion-toolbar .toolbar-title {
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
ion-footer .toolbar:last-child {
|
||||
padding-bottom: 4px;
|
||||
min-height: 0;
|
||||
}
|
||||
}
|
|
@ -19,7 +19,9 @@ import {
|
|||
AddonMessages,
|
||||
AddonMessagesDiscussion,
|
||||
AddonMessagesMessageAreaContact,
|
||||
AddonMessagesNewMessagedEventData,
|
||||
AddonMessagesProvider,
|
||||
AddonMessagesReadChangedEventData,
|
||||
AddonMessagesSplitViewLoadIndexEventData,
|
||||
} from '../../services/messages';
|
||||
import { CoreDomUtils } from '@services/utils/dom';
|
||||
|
@ -78,38 +80,46 @@ export class AddonMessagesDiscussions35Page implements OnInit, OnDestroy {
|
|||
this.siteId = CoreSites.instance.getCurrentSiteId();
|
||||
|
||||
// Update discussions when new message is received.
|
||||
this.newMessagesObserver = CoreEvents.on(AddonMessagesProvider.NEW_MESSAGE_EVENT, (data: any) => {
|
||||
if (data.userId && this.discussions) {
|
||||
const discussion = this.discussions.find((disc) => disc.message!.user == data.userId);
|
||||
this.newMessagesObserver = CoreEvents.on<AddonMessagesNewMessagedEventData>(
|
||||
AddonMessagesProvider.NEW_MESSAGE_EVENT,
|
||||
(data) => {
|
||||
if (data.userId && this.discussions) {
|
||||
const discussion = this.discussions.find((disc) => disc.message!.user == data.userId);
|
||||
|
||||
if (typeof discussion == 'undefined') {
|
||||
this.loaded = false;
|
||||
this.refreshData().finally(() => {
|
||||
this.loaded = true;
|
||||
});
|
||||
} else {
|
||||
if (typeof discussion == 'undefined') {
|
||||
this.loaded = false;
|
||||
this.refreshData().finally(() => {
|
||||
this.loaded = true;
|
||||
});
|
||||
} else {
|
||||
// An existing discussion has a new message, update the last message.
|
||||
discussion.message!.message = data.message;
|
||||
discussion.message!.timecreated = data.timecreated;
|
||||
discussion.message!.message = data.message;
|
||||
discussion.message!.timecreated = data.timecreated;
|
||||
}
|
||||
}
|
||||
}
|
||||
}, this.siteId);
|
||||
},
|
||||
this.siteId,
|
||||
);
|
||||
|
||||
// Update discussions when a message is read.
|
||||
this.readChangedObserver = CoreEvents.on(AddonMessagesProvider.READ_CHANGED_EVENT, (data: any) => {
|
||||
if (data.userId && this.discussions) {
|
||||
const discussion = this.discussions.find((disc) => disc.message!.user == data.userId);
|
||||
this.readChangedObserver = CoreEvents.on<AddonMessagesReadChangedEventData>(
|
||||
AddonMessagesProvider.READ_CHANGED_EVENT,
|
||||
(data) => {
|
||||
if (data.userId && this.discussions) {
|
||||
const discussion = this.discussions.find((disc) => disc.message!.user == data.userId);
|
||||
|
||||
if (typeof discussion != 'undefined') {
|
||||
if (typeof discussion != 'undefined') {
|
||||
// A discussion has been read reset counter.
|
||||
discussion.unread = false;
|
||||
discussion.unread = false;
|
||||
|
||||
// Conversations changed, invalidate them and refresh unread counts.
|
||||
AddonMessages.instance.invalidateConversations(this.siteId);
|
||||
AddonMessages.instance.refreshUnreadConversationCounts(this.siteId);
|
||||
// Conversations changed, invalidate them and refresh unread counts.
|
||||
AddonMessages.instance.invalidateConversations(this.siteId);
|
||||
AddonMessages.instance.refreshUnreadConversationCounts(this.siteId);
|
||||
}
|
||||
}
|
||||
}
|
||||
}, this.siteId);
|
||||
},
|
||||
this.siteId,
|
||||
);
|
||||
|
||||
// Update unread conversation counts.
|
||||
this.cronObserver = CoreEvents.on(AddonMessagesProvider.UNREAD_CONVERSATION_COUNTS_EVENT, () => {
|
||||
|
|
|
@ -23,6 +23,10 @@ import {
|
|||
AddonMessagesMemberInfoChangedEventData,
|
||||
AddonMessagesContactRequestCountEventData,
|
||||
AddonMessagesUnreadConversationCountsEventData,
|
||||
AddonMessagesReadChangedEventData,
|
||||
AddonMessagesUpdateConversationListEventData,
|
||||
AddonMessagesNewMessagedEventData,
|
||||
AddonMessagesOpenConversationEventData,
|
||||
} from '../../services/messages';
|
||||
import { AddonMessagesOffline } from '../../services/messages-offline';
|
||||
import { CoreDomUtils } from '@services/utils/dom';
|
||||
|
@ -112,47 +116,53 @@ export class AddonMessagesGroupConversationsPage implements OnInit, OnDestroy {
|
|||
this.currentUserId = CoreSites.instance.getCurrentSiteUserId();
|
||||
|
||||
// Update conversations when new message is received.
|
||||
this.newMessagesObserver = CoreEvents.on(AddonMessagesProvider.NEW_MESSAGE_EVENT, (data: any) => {
|
||||
this.newMessagesObserver = CoreEvents.on<AddonMessagesNewMessagedEventData>(
|
||||
AddonMessagesProvider.NEW_MESSAGE_EVENT,
|
||||
(data) => {
|
||||
// Check if the new message belongs to the option that is currently expanded.
|
||||
const expandedOption = this.getExpandedOption();
|
||||
const messageOption = this.getConversationOption(data);
|
||||
const expandedOption = this.getExpandedOption();
|
||||
const messageOption = this.getConversationOption(data);
|
||||
|
||||
if (expandedOption != messageOption) {
|
||||
return; // Message doesn't belong to current list, stop.
|
||||
}
|
||||
|
||||
// Search the conversation to update.
|
||||
const conversation = this.findConversation(data.conversationId, data.userId, expandedOption);
|
||||
|
||||
if (typeof conversation == 'undefined') {
|
||||
// Probably a new conversation, refresh the list.
|
||||
this.loaded = false;
|
||||
this.refreshData().finally(() => {
|
||||
this.loaded = true;
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
if (conversation.lastmessage != data.message || conversation.lastmessagedate != data.timecreated / 1000) {
|
||||
const isNewer = data.timecreated / 1000 > (conversation.lastmessagedate || 0);
|
||||
|
||||
// An existing conversation has a new message, update the last message.
|
||||
conversation.lastmessage = data.message;
|
||||
conversation.lastmessagedate = data.timecreated / 1000;
|
||||
|
||||
// Sort the affected list.
|
||||
const option = this.getConversationOption(conversation);
|
||||
option.conversations = AddonMessages.instance.sortConversations(option.conversations || []);
|
||||
|
||||
if (isNewer) {
|
||||
// The last message is newer than the previous one, scroll to top to keep viewing the conversation.
|
||||
this.content?.scrollToTop();
|
||||
if (expandedOption != messageOption) {
|
||||
return; // Message doesn't belong to current list, stop.
|
||||
}
|
||||
}
|
||||
}, this.siteId);
|
||||
|
||||
// Search the conversation to update.
|
||||
const conversation = this.findConversation(data.conversationId, data.userId, expandedOption);
|
||||
|
||||
if (typeof conversation == 'undefined') {
|
||||
// Probably a new conversation, refresh the list.
|
||||
this.loaded = false;
|
||||
this.refreshData().finally(() => {
|
||||
this.loaded = true;
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
if (conversation.lastmessage != data.message || conversation.lastmessagedate != data.timecreated / 1000) {
|
||||
const isNewer = data.timecreated / 1000 > (conversation.lastmessagedate || 0);
|
||||
|
||||
// An existing conversation has a new message, update the last message.
|
||||
conversation.lastmessage = data.message;
|
||||
conversation.lastmessagedate = data.timecreated / 1000;
|
||||
|
||||
// Sort the affected list.
|
||||
const option = this.getConversationOption(conversation);
|
||||
option.conversations = AddonMessages.instance.sortConversations(option.conversations || []);
|
||||
|
||||
if (isNewer) {
|
||||
// The last message is newer than the previous one, scroll to top to keep viewing the conversation.
|
||||
this.content?.scrollToTop();
|
||||
}
|
||||
}
|
||||
},
|
||||
this.siteId,
|
||||
);
|
||||
|
||||
// Update conversations when a message is read.
|
||||
this.readChangedObserver = CoreEvents.on(AddonMessagesProvider.READ_CHANGED_EVENT, (data: any) => {
|
||||
this.readChangedObserver = CoreEvents.on<AddonMessagesReadChangedEventData>(AddonMessagesProvider.READ_CHANGED_EVENT, (
|
||||
data,
|
||||
) => {
|
||||
if (data.conversationId) {
|
||||
const conversation = this.findConversation(data.conversationId);
|
||||
|
||||
|
@ -168,11 +178,15 @@ export class AddonMessagesGroupConversationsPage implements OnInit, OnDestroy {
|
|||
}, this.siteId);
|
||||
|
||||
// Load a discussion if we receive an event to do so.
|
||||
this.openConversationObserver = CoreEvents.on(AddonMessagesProvider.OPEN_CONVERSATION_EVENT, (data: any) => {
|
||||
if (data.conversationId || data.userId) {
|
||||
this.gotoConversation(data.conversationId, data.userId);
|
||||
}
|
||||
}, this.siteId);
|
||||
this.openConversationObserver = CoreEvents.on<AddonMessagesOpenConversationEventData>(
|
||||
AddonMessagesProvider.OPEN_CONVERSATION_EVENT,
|
||||
(data) => {
|
||||
if (data.conversationId || data.userId) {
|
||||
this.gotoConversation(data.conversationId, data.userId);
|
||||
}
|
||||
},
|
||||
this.siteId,
|
||||
);
|
||||
|
||||
// Refresh the view when the app is resumed.
|
||||
this.appResumeSubscription = Platform.instance.resume.subscribe(() => {
|
||||
|
@ -186,24 +200,28 @@ export class AddonMessagesGroupConversationsPage implements OnInit, OnDestroy {
|
|||
});
|
||||
|
||||
// Update conversations if we receive an event to do so.
|
||||
this.updateConversationListObserver = CoreEvents.on(AddonMessagesProvider.UPDATE_CONVERSATION_LIST_EVENT, (data: any) => {
|
||||
if (data && data.action == 'mute') {
|
||||
this.updateConversationListObserver = CoreEvents.on<AddonMessagesUpdateConversationListEventData>(
|
||||
AddonMessagesProvider.UPDATE_CONVERSATION_LIST_EVENT,
|
||||
(data) => {
|
||||
if (data && data.action == 'mute') {
|
||||
// If the conversation is displayed, change its muted value.
|
||||
const expandedOption = this.getExpandedOption();
|
||||
const expandedOption = this.getExpandedOption();
|
||||
|
||||
if (expandedOption && expandedOption.conversations) {
|
||||
const conversation = this.findConversation(data.conversationId, undefined, expandedOption);
|
||||
if (conversation) {
|
||||
conversation.ismuted = data.value;
|
||||
if (expandedOption && expandedOption.conversations) {
|
||||
const conversation = this.findConversation(data.conversationId, undefined, expandedOption);
|
||||
if (conversation) {
|
||||
conversation.ismuted = !!data.value;
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
this.refreshData();
|
||||
|
||||
this.refreshData();
|
||||
|
||||
}, this.siteId);
|
||||
},
|
||||
this.siteId,
|
||||
);
|
||||
|
||||
// If a message push notification is received, refresh the view.
|
||||
this.pushObserver = CorePushNotificationsDelegate.instance.on<CorePushNotificationsNotificationBasicData>('receive')
|
||||
|
@ -667,7 +685,9 @@ export class AddonMessagesGroupConversationsPage implements OnInit, OnDestroy {
|
|||
* @param conversation Conversation to check.
|
||||
* @return Option object.
|
||||
*/
|
||||
protected getConversationOption(conversation: AddonMessagesConversationForList): AddonMessagesGroupConversationOption {
|
||||
protected getConversationOption(
|
||||
conversation: AddonMessagesConversationForList | AddonMessagesNewMessagedEventData,
|
||||
): AddonMessagesGroupConversationOption {
|
||||
if (conversation.isfavourite) {
|
||||
return this.favourites;
|
||||
}
|
||||
|
|
|
@ -10,7 +10,8 @@
|
|||
</ion-buttons>
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
<!-- @todo <core-split-view> -->
|
||||
<ion-content>
|
||||
<core-tabs [tabs]="tabs" hideUntil="true"></core-tabs>
|
||||
</ion-content>
|
||||
<core-split-view>
|
||||
<ion-content>
|
||||
<core-tabs [tabs]="tabs" hideUntil="true"></core-tabs>
|
||||
</ion-content>
|
||||
</core-split-view>
|
||||
|
|
|
@ -251,19 +251,18 @@ export class AddonMessagesProvider {
|
|||
* @param deleteForAll Whether the message should be deleted for all users.
|
||||
* @return Promise resolved when the message has been deleted.
|
||||
*/
|
||||
deleteMessage(message: any, deleteForAll?: boolean): Promise<void> {
|
||||
// @todo Add message type.
|
||||
if (message.id) {
|
||||
deleteMessage(message: AddonMessagesConversationMessageFormatted, deleteForAll?: boolean): Promise<void> {
|
||||
if ('id' in message) {
|
||||
// Message has ID, it means it has been sent to the server.
|
||||
if (deleteForAll) {
|
||||
return this.deleteMessageForAllOnline(message.id);
|
||||
} else {
|
||||
return this.deleteMessageOnline(message.id, message.read);
|
||||
return this.deleteMessageOnline(message.id, !!('read' in message && message.read));
|
||||
}
|
||||
}
|
||||
|
||||
// It's an offline message.
|
||||
if (!message.conversationid) {
|
||||
if (!('conversationid' in message)) {
|
||||
return AddonMessagesOffline.instance.deleteMessage(message.touserid, message.smallmessage, message.timecreated);
|
||||
}
|
||||
|
||||
|
@ -1433,7 +1432,7 @@ export class AddonMessagesProvider {
|
|||
const response: AddonMessagesGetMessagesResult = await site.read('core_message_get_messages', params, preSets);
|
||||
|
||||
response.messages.forEach((message) => {
|
||||
message.read = !params.read ? 0 : 1;
|
||||
message.read = !!params.read;
|
||||
// Convert times to milliseconds.
|
||||
message.timecreated = message.timecreated ? message.timecreated * 1000 : 0;
|
||||
message.timeread = message.timeread ? message.timeread * 1000 : 0;
|
||||
|
@ -2786,6 +2785,9 @@ export class AddonMessagesProvider {
|
|||
* @param messages Array of messages containing the key 'timecreated'.
|
||||
* @return Messages sorted with most recent last.
|
||||
*/
|
||||
sortMessages(
|
||||
messages: AddonMessagesConversationMessageFormatted[],
|
||||
): AddonMessagesConversationMessageFormatted[];
|
||||
sortMessages(
|
||||
messages: (AddonMessagesGetMessagesMessage | AddonMessagesOfflineMessagesDBRecordFormatted)[],
|
||||
): (AddonMessagesGetMessagesMessage | AddonMessagesOfflineMessagesDBRecordFormatted)[];
|
||||
|
@ -2794,9 +2796,11 @@ export class AddonMessagesProvider {
|
|||
): (AddonMessagesOfflineMessagesDBRecordFormatted | AddonMessagesOfflineConversationMessagesDBRecordFormatted)[];
|
||||
sortMessages(
|
||||
messages: (AddonMessagesGetMessagesMessage | AddonMessagesOfflineMessagesDBRecordFormatted)[] |
|
||||
(AddonMessagesOfflineMessagesDBRecordFormatted | AddonMessagesOfflineConversationMessagesDBRecordFormatted)[],
|
||||
(AddonMessagesOfflineMessagesDBRecordFormatted | AddonMessagesOfflineConversationMessagesDBRecordFormatted)[] |
|
||||
AddonMessagesConversationMessageFormatted[],
|
||||
): (AddonMessagesGetMessagesMessage | AddonMessagesOfflineMessagesDBRecordFormatted)[] |
|
||||
(AddonMessagesOfflineMessagesDBRecordFormatted | AddonMessagesOfflineConversationMessagesDBRecordFormatted)[] {
|
||||
(AddonMessagesOfflineMessagesDBRecordFormatted | AddonMessagesOfflineConversationMessagesDBRecordFormatted)[] |
|
||||
AddonMessagesConversationMessageFormatted[] {
|
||||
return messages.sort((a, b) => {
|
||||
// Pending messages last.
|
||||
if (a.pending && !b.pending) {
|
||||
|
@ -3041,6 +3045,23 @@ export type AddonMessagesConversationMessage = {
|
|||
timecreated: number; // The timecreated timestamp for the message.
|
||||
};
|
||||
|
||||
/**
|
||||
* Conversation message with some calculated data.
|
||||
*/
|
||||
export type AddonMessagesConversationMessageFormatted =
|
||||
(AddonMessagesConversationMessage
|
||||
| AddonMessagesGetMessagesMessage
|
||||
| AddonMessagesOfflineMessagesDBRecordFormatted
|
||||
| AddonMessagesOfflineConversationMessagesDBRecordFormatted) & {
|
||||
pending?: boolean; // Calculated in the app. Whether the message is pending to be sent.
|
||||
sending?: boolean; // Calculated in the app. Whether the message is being sent right now.
|
||||
hash?: string; // Calculated in the app. A hash to identify the message.
|
||||
showDate?: boolean; // Calculated in the app. Whether to show the date before the message.
|
||||
showUserData?: boolean; // Calculated in the app. Whether to show the user data in the message.
|
||||
showTail?: boolean; // Calculated in the app. Whether to show a "tail" in the message.
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Data returned by core_message_get_user_message_preferences WS.
|
||||
*/
|
||||
|
@ -3417,7 +3438,7 @@ export type AddonMessagesMessagePreferencesCalculatedData = {
|
|||
*/
|
||||
export type AddonMessagesGetMessagesMessageCalculatedData = {
|
||||
pending?: boolean; // Calculated in the app. Whether the message is pending to be sent.
|
||||
read?: number; // Calculated in the app. Whether the message has been read.
|
||||
read?: boolean; // Calculated in the app. Whether the message has been read.
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -3691,3 +3712,40 @@ export type AddonMessagesSplitViewLoadContactsEventData = {
|
|||
userId: number;
|
||||
onInit: boolean;
|
||||
};
|
||||
|
||||
/**
|
||||
* Data sent by READ_CHANGED_EVENT event.
|
||||
*/
|
||||
export type AddonMessagesReadChangedEventData = {
|
||||
userId?: number;
|
||||
conversationId?: number;
|
||||
};
|
||||
|
||||
/**
|
||||
* Data sent by NEW_MESSAGE_EVENT event.
|
||||
*/
|
||||
export type AddonMessagesNewMessagedEventData = {
|
||||
conversationId?: number;
|
||||
userId?: number;
|
||||
message: string;
|
||||
timecreated: number;
|
||||
isfavourite: boolean;
|
||||
type?: number;
|
||||
};
|
||||
|
||||
/**
|
||||
* Data sent by UPDATE_CONVERSATION_LIST_EVENT event.
|
||||
*/
|
||||
export type AddonMessagesUpdateConversationListEventData = {
|
||||
conversationId: number;
|
||||
action: string;
|
||||
value?: boolean;
|
||||
};
|
||||
|
||||
/**
|
||||
* Data sent by OPEN_CONVERSATION_EVENT event.
|
||||
*/
|
||||
export type AddonMessagesOpenConversationEventData = {
|
||||
userId?: number;
|
||||
conversationId?: number;
|
||||
};
|
||||
|
|
|
@ -22,6 +22,7 @@ import { CoreMainMenuHandler, CoreMainMenuHandlerData } from '@features/mainmenu
|
|||
import { CorePushNotifications } from '@features/pushnotifications/services/pushnotifications';
|
||||
import { CorePushNotificationsDelegate } from '@features/pushnotifications/services/push-delegate';
|
||||
import { AddonNotifications, AddonNotificationsProvider } from '../notifications';
|
||||
import { AddonMessagesReadChangedEventData } from '@addons/messages/services/messages';
|
||||
|
||||
/**
|
||||
* Handler to inject an option into main menu.
|
||||
|
@ -48,7 +49,7 @@ export class AddonNotificationsMainMenuHandlerService implements CoreMainMenuHan
|
|||
* Initialize the handler.
|
||||
*/
|
||||
initialize(): void {
|
||||
CoreEvents.on(AddonNotificationsProvider.READ_CHANGED_EVENT, (data: CoreEventSiteData) => {
|
||||
CoreEvents.on<AddonMessagesReadChangedEventData>(AddonNotificationsProvider.READ_CHANGED_EVENT, (data) => {
|
||||
this.updateBadge(data.siteId);
|
||||
});
|
||||
|
||||
|
|
|
@ -6,6 +6,13 @@
|
|||
width: var(--core-avatar-size);
|
||||
height: var(--core-avatar-size);
|
||||
}
|
||||
&.core-bar-button-image img {
|
||||
padding: 0;
|
||||
width: var(--core-toolbar-button-image-width);
|
||||
height: var(--core-toolbar-button-image-width);
|
||||
max-width: var(--core-toolbar-button-image-width);
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.contact-status {
|
||||
position: absolute;
|
||||
|
|
|
@ -17,6 +17,7 @@ import { CoreSites } from '@services/sites';
|
|||
import { CoreSite, CoreSiteWSPreSets } from '@classes/site';
|
||||
import { CoreWSExternalWarning } from '@services/ws';
|
||||
import { makeSingleton, Translate } from '@singletons';
|
||||
import { CoreError } from '@classes/errors/error';
|
||||
|
||||
const ROOT_CACHE_KEY = 'CoreTag:';
|
||||
|
||||
|
@ -121,7 +122,7 @@ export class CoreTagProvider {
|
|||
const response: CoreTagCollections = await site.read('core_tag_get_tag_collections', null, preSets);
|
||||
|
||||
if (!response || !response.collections) {
|
||||
throw null;
|
||||
throw new CoreError('Cannot fetch tag collections');
|
||||
}
|
||||
|
||||
return response.collections;
|
||||
|
@ -185,7 +186,7 @@ export class CoreTagProvider {
|
|||
}
|
||||
|
||||
if (!response) {
|
||||
throw null;
|
||||
throw new CoreError('Cannot fetch tag index per area');
|
||||
}
|
||||
|
||||
return response;
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
// limitations under the License.
|
||||
|
||||
import { Injectable } from '@angular/core';
|
||||
import { ActivatedRoute, Params } from '@angular/router';
|
||||
import { ActivatedRoute, NavigationStart, Params, Router as RouterService } from '@angular/router';
|
||||
|
||||
import { NavigationOptions } from '@ionic/angular/providers/nav-controller';
|
||||
|
||||
|
@ -27,6 +27,8 @@ import { CoreUtils } from '@services/utils/utils';
|
|||
import { CoreUrlUtils } from '@services/utils/url';
|
||||
import { CoreTextUtils } from '@services/utils/text';
|
||||
import { makeSingleton, NavController, Router } from '@singletons';
|
||||
import { CoreScreen } from './screen';
|
||||
import { filter } from 'rxjs/operators';
|
||||
|
||||
const DEFAULT_MAIN_MENU_TAB = CoreMainMenuHomeHandlerService.PAGE_NAME;
|
||||
|
||||
|
@ -55,6 +57,17 @@ export class CoreNavigatorService {
|
|||
|
||||
protected storedParams: Record<number, unknown> = {};
|
||||
protected lastParamId = 0;
|
||||
protected currentPath?: string;
|
||||
protected previousPath?: string;
|
||||
|
||||
// @todo Param router is an optional param to let the mocking work.
|
||||
constructor(router?: RouterService) {
|
||||
router?.events.pipe(filter(event => event instanceof NavigationStart)).subscribe((routerEvent: NavigationStart) => {
|
||||
// Using NavigationStart instead of NavigationEnd so it can be check on ngOnInit.
|
||||
this.previousPath = this.currentPath;
|
||||
this.currentPath = routerEvent.url;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether the active route is using the given path.
|
||||
|
@ -194,6 +207,15 @@ export class CoreNavigatorService {
|
|||
return CoreUrlUtils.instance.removeUrlParams(Router.instance.url);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the previous navigation route path.
|
||||
*
|
||||
* @return Previous path.
|
||||
*/
|
||||
getPreviousPath(): string {
|
||||
return CoreUrlUtils.instance.removeUrlParams(this.previousPath || '');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a parameter for the current route.
|
||||
* Please notice that objects can only be retrieved once. You must call this function only once per page and parameter,
|
||||
|
|
|
@ -309,6 +309,15 @@ ion-item img.core-module-icon[slot="start"] {
|
|||
margin-left: 32px;
|
||||
}
|
||||
|
||||
ion-toolbar ion-title img.core-bar-button-image,
|
||||
ion-toolbar ion-title .core-bar-button-image img {
|
||||
padding: 0;
|
||||
width: var(--core-toolbar-button-image-width);
|
||||
height: var(--core-toolbar-button-image-width);
|
||||
max-width: var(--core-toolbar-button-image-width);
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
// Action sheet.
|
||||
.md ion-action-sheet {
|
||||
.action-sheet-group-cancel {
|
||||
|
|
|
@ -97,6 +97,7 @@
|
|||
|
||||
ion-content {
|
||||
--background: var(--gray-light);
|
||||
--background-lighter: var(--gray-lighter);
|
||||
--contrast-background: var(--white);
|
||||
}
|
||||
|
||||
|
@ -190,6 +191,11 @@
|
|||
|
||||
--core-avatar-size: var(--custom-avatar-size, 40px);
|
||||
|
||||
--core-toolbar-button-image-width: var(--custom-toolbar-button-image-width, 32px);
|
||||
|
||||
--core-send-message-input-background: var(--custom-send-message-input-background, var(--gray));
|
||||
--core-send-message-input-color: var(--custom-send-message-input-color, var(--black));
|
||||
|
||||
--addon-calendar-event-category-color: var(--custom-calendar-event-category-color, var(--purple));
|
||||
--addon-calendar-event-course-color: var(--custom-calendar-event-course-color, var(--red));
|
||||
--addon-calendar-event-group-color: var(--custom-calendar-event-group-color, var(--yellow));
|
||||
|
@ -198,6 +204,16 @@
|
|||
--addon-calendar-today-bgcolor: var(--custom-calendar-today-bgcolor, var(--core-color));
|
||||
--addon-calendar-today-color: var(--custom-calendar-today-color, var(--white));
|
||||
--addon-calendar-border-color: var(--custom-calendar-border-color, var(--gray));
|
||||
|
||||
--addon-messages-message-bg: var(--custom-messages-message-bg, var(--white));
|
||||
--addon-messages-message-activated-bg: var(--custom-messages-message-activated-bg, var(--gray-light));
|
||||
--addon-messages-message-note-text: var(--custom-messages-message-note-text, var(--gray-dark));
|
||||
--addon-messages-message-note-font-size: var(--custom-messages-message-note-font-size, 75%);
|
||||
--addon-messages-message-mine-bg: var(--custom-messages-message-mine-bg, var(--gray-light));
|
||||
--addon-messages-message-mine-activated-bg: var(--custom-messages-message-mine-activated-bg, var(--gray));
|
||||
--addon-messages-avatar-size: var(--custom-messages-avatar-size, 30px);
|
||||
--addon-messages-discussion-badge: var(--custom-messages-discussion-badge, var(--core-color));
|
||||
--addon-messages-discussion-badge-text: var(--custom-messages-discussion-badge-text, var(--white));
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -255,6 +271,7 @@
|
|||
|
||||
ion-content {
|
||||
--background: var(--ion-background-color);
|
||||
--background-lighter: var(--gray-darker);
|
||||
--contrast-background: var(--ion-background-color);
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue