diff --git a/scripts/langindex.json b/scripts/langindex.json index e8a555bb2..0ed0824bc 100644 --- a/scripts/langindex.json +++ b/scripts/langindex.json @@ -433,14 +433,17 @@ "addon.mod_chat.errorwhilegettingchatusers": "local_moodlemobileapp", "addon.mod_chat.errorwhileretrievingmessages": "local_moodlemobileapp", "addon.mod_chat.errorwhilesendingmessage": "local_moodlemobileapp", + "addon.mod_chat.messagebeepseveryone": "chat", "addon.mod_chat.messagebeepsyou": "chat", "addon.mod_chat.messageenter": "chat", "addon.mod_chat.messageexit": "chat", "addon.mod_chat.messages": "chat", + "addon.mod_chat.messageyoubeep": "chat", "addon.mod_chat.modulenameplural": "chat", "addon.mod_chat.mustbeonlinetosendmessages": "local_moodlemobileapp", "addon.mod_chat.nomessages": "chat", "addon.mod_chat.nosessionsfound": "local_moodlemobileapp", + "addon.mod_chat.saidto": "chat", "addon.mod_chat.send": "chat", "addon.mod_chat.sessionstart": "chat", "addon.mod_chat.showincompletesessions": "local_moodlemobileapp", diff --git a/src/addon/messages/pages/discussion/discussion.scss b/src/addon/messages/pages/discussion/discussion.scss index 5b8ee2a35..0c2237fc2 100644 --- a/src/addon/messages/pages/discussion/discussion.scss +++ b/src/addon/messages/pages/discussion/discussion.scss @@ -4,8 +4,7 @@ $item-message-note-text: $gray-dark !default; $item-message-note-font-size: 75% !default; $item-message-mine-bg: $gray-light !default; -ion-app.app-root page-addon-messages-discussion.ion-page { - +@mixin message-page { ion-content { background-color: $gray-lighter !important; @include darkmode() { @@ -35,7 +34,7 @@ ion-app.app-root page-addon-messages-discussion.ion-page { } // Message item. - .addon-message { + .item.item-block.addon-message { border: 0; border-radius: 4px; padding: 8px; @@ -60,7 +59,7 @@ ion-app.app-root page-addon-messages-discussion.ion-page { flex-direction: row; justify-content: space-between; align-items: center; - margin-bottom: .5rem!important; + margin-bottom: .5rem; margin-top: 0; color: $text-color; @@ -99,7 +98,7 @@ ion-app.app-root page-addon-messages-discussion.ion-page { background-color: darken($item-message-bg, 10%); } - &.item-block .item-inner { + .item-inner { border-bottom: 0; padding: 0; margin: 0; @@ -131,6 +130,7 @@ ion-app.app-root page-addon-messages-discussion.ion-page { .icon { font-size: 1.4em; line-height: initial; + color: $danger; } } @@ -142,10 +142,50 @@ ion-app.app-root page-addon-messages-discussion.ion-page { position: absolute; touch-action: none; } + + // Defines when an item-message is the user's. + &.addon-message-mine { + background-color: $item-message-mine-bg; + align-self: flex-end; + + &.activated { + background-color: darken($item-message-mine-bg, 10%); + } + + .spinner { + @include float(end); + @include margin(2px, -3px, -2px, 5px); + + svg { + width: 16px; + height: 16px; + } + } + + .tail { + @include position(null, 0, 0, null); + @include margin-horizontal(null, -0.5rem); + border-bottom-color: $item-message-mine-bg; + } + + &.activated .tail { + border-bottom-color: darken($item-message-mine-bg, 10%); + } + } + + &.addon-message-not-mine .tail { + @include position(null, null, 0, 0); + @include margin-horizontal(-0.5rem, null); + border-bottom-color: $item-message-bg; + } + + &.addon-message-not-mine.activated .tail { + border-bottom-color: darken($item-message-bg, 10%); + } } - .addon-message.addon-message-mine + .addon-message-no-user.addon-message-mine, - .addon-message.addon-message-not-mine + .addon-message-no-user.addon-message-not-mine { + .item.addon-message.addon-message-mine + .item.addon-message.addon-message-no-user.addon-message-mine, + .item.addon-message.addon-message-not-mine + .item.addon-message.addon-message-no-user.addon-message-not-mine { h2 { margin-bottom: 0; } @@ -154,46 +194,12 @@ ion-app.app-root page-addon-messages-discussion.ion-page { border-top-right-radius: 0; border-top-left-radius: 0; } +} - // Defines when an item-message is the user's. - .addon-message-mine { - background-color: $item-message-mine-bg; - align-self: flex-end; - &.activated { - background-color: darken($item-message-mine-bg, 10%); - } +ion-app.app-root page-addon-messages-discussion.ion-page { - .spinner { - @include float(end); - @include margin(2px, -3px, -2px, 5px); - - svg { - width: 16px; - height: 16px; - } - } - - .tail { - @include position(null, 0, 0, null); - @include margin-horizontal(null, -0.5rem); - border-bottom-color: $item-message-mine-bg; - } - - &.activated .tail { - border-bottom-color: darken($item-message-mine-bg, 10%); - } - } - - .addon-message-not-mine .tail { - @include position(null, null, 0, 0); - @include margin-horizontal(-0.5rem, null); - border-bottom-color: $item-message-bg; - } - - .addon-message-not-mine.activated .tail { - border-bottom-color: darken($item-message-bg, 10%); - } + @include message-page(); .toolbar-title { padding: 0; @@ -224,4 +230,4 @@ ion-app.app-root page-addon-messages-discussion.ion-page { ion-app.app-root.ios page-addon-messages-discussion ion-footer .toolbar:last-child { padding-bottom: 4px; min-height: 0; -} \ No newline at end of file +} diff --git a/src/addon/mod/chat/chat.module.ts b/src/addon/mod/chat/chat.module.ts index 7d90566e0..de0349837 100644 --- a/src/addon/mod/chat/chat.module.ts +++ b/src/addon/mod/chat/chat.module.ts @@ -18,6 +18,7 @@ import { CoreCourseModuleDelegate } from '@core/course/providers/module-delegate import { CoreCourseModulePrefetchDelegate } from '@core/course/providers/module-prefetch-delegate'; import { AddonModChatComponentsModule } from './components/components.module'; import { AddonModChatProvider } from './providers/chat'; +import { AddonModChatHelperProvider } from './providers/helper'; import { AddonModChatLinkHandler } from './providers/link-handler'; import { AddonModChatListLinkHandler } from './providers/list-link-handler'; import { AddonModChatModuleHandler } from './providers/module-handler'; @@ -36,6 +37,7 @@ export const ADDON_MOD_CHAT_PROVIDERS: any[] = [ ], providers: [ AddonModChatProvider, + AddonModChatHelperProvider, AddonModChatLinkHandler, AddonModChatListLinkHandler, AddonModChatModuleHandler, diff --git a/src/addon/mod/chat/lang/en.json b/src/addon/mod/chat/lang/en.json index 4348b5d63..ad8f86ce8 100644 --- a/src/addon/mod/chat/lang/en.json +++ b/src/addon/mod/chat/lang/en.json @@ -9,14 +9,17 @@ "errorwhilegettingchatusers": "Error while getting chat users.", "errorwhileretrievingmessages": "Error while retrieving messages from the server.", "errorwhilesendingmessage": "Error while sending the message.", + "messagebeepseveryone": "{{$a}} beeps everyone!", "messagebeepsyou": "{{$a}} has just beeped you!", "messageenter": "{{$a}} has just entered this chat", "messageexit": "{{$a}} has left this chat", "messages": "Messages", + "messageyoubeep": "You beeped {{$a}}", "modulenameplural": "Chats", "mustbeonlinetosendmessages": "You must be online to send messages.", "nomessages": "No messages yet", "nosessionsfound": "No sessions found", + "saidto": "said to", "send": "Send", "sessionstart": "The next chat session will start on {{$a.date}}, ({{$a.fromnow}} from now)", "showincompletesessions": "Show incomplete sessions", diff --git a/src/addon/mod/chat/pages/chat/chat.html b/src/addon/mod/chat/pages/chat/chat.html index 47f744618..220b2bab0 100644 --- a/src/addon/mod/chat/pages/chat/chat.html +++ b/src/addon/mod/chat/pages/chat/chat.html @@ -8,55 +8,68 @@ - + -
-
+ + -
- - {{ message.timestamp * 1000 | coreFormatDate:"strftimedayshort" }} +
+ {{ message.timestamp * 1000 | coreFormatDate:"strftimedayshort" }} +
+ +
+ + {{ message.timestamp * 1000 | coreFormatDate:"strftimetime" }} {{ 'addon.mod_chat.messageenter' | translate:{$a: message.userfullname} }} + + + + {{ message.timestamp * 1000 | coreFormatDate:"strftimetime" }} {{ 'addon.mod_chat.messageexit' | translate:{$a: message.userfullname} }} + + + + {{ message.timestamp * 1000 | coreFormatDate:"strftimetime" }} + {{ 'addon.mod_chat.messagebeepseveryone' | translate:{$a: message.userfullname} }} + + + + {{ message.timestamp * 1000 | coreFormatDate:"strftimetime" }} + {{ 'addon.mod_chat.messagebeepsyou' | translate:{$a: message.userfullname} }} + + + + {{ message.timestamp * 1000 | coreFormatDate:"strftimetime" }} + {{ 'addon.mod_chat.messageyoubeep' | translate:{$a: message.beepWho} }} + + + + {{ message.timestamp * 1000 | coreFormatDate:"strftimetime" }} + {{ message.userfullname }}
-
- - {{ message.timestamp * 1000 | coreFormatDate:"strftimetime" }} {{ 'addon.mod_chat.messageenter' | translate:{$a: message.userfullname} }} - -
- -
- - {{ message.timestamp * 1000 | coreFormatDate:"strftimetime" }} {{ 'addon.mod_chat.messageexit' | translate:{$a: message.userfullname} }} - -
- -
- - {{ 'addon.mod_chat.messagebeepsyou' | translate:{$a: message.userfullname} }} - -
- - - -

-

{{ message.timestamp * 1000 | coreFormatDate:"strftimetime" }}

- + + +

+ +
{{ message.userfullname }}
+ {{ message.timestamp * 1000 | coreFormatDate: "strftimetime" }}

- -
-

-
-

{{ 'addon.mod_chat.nomessages' | translate}}

-
-
+

+ +

+
+ + + + +

{{ 'addon.mod_chat.mustbeonlinetosendmessages' | translate }}

- +
diff --git a/src/addon/mod/chat/pages/chat/chat.scss b/src/addon/mod/chat/pages/chat/chat.scss index fe62c5619..e9eb2ef73 100644 --- a/src/addon/mod/chat/pages/chat/chat.scss +++ b/src/addon/mod/chat/pages/chat/chat.scss @@ -1,9 +1,8 @@ -ion-app.app-root page-addon-mod-chat-chat { +ion-app.app-root page-addon-mod-chat-chat.ion-page { + @include message-page(); + .addon-mod-chat-notice { margin-top: 10px; margin-bottom: 10px; } - .addon-mod-chat-message { - align-items: flex-start; - } } diff --git a/src/addon/mod/chat/pages/chat/chat.ts b/src/addon/mod/chat/pages/chat/chat.ts index 47be73f24..18f5ce956 100644 --- a/src/addon/mod/chat/pages/chat/chat.ts +++ b/src/addon/mod/chat/pages/chat/chat.ts @@ -19,10 +19,11 @@ import { CoreEventsProvider } from '@providers/events'; import { CoreLoggerProvider } from '@providers/logger'; import { CoreSitesProvider } from '@providers/sites'; import { CoreDomUtilsProvider } from '@providers/utils/dom'; -import { CoreTextUtilsProvider } from '@providers/utils/text'; -import { AddonModChatProvider, AddonModChatMessageWithUserData } from '../../providers/chat'; +import { AddonModChatProvider } from '../../providers/chat'; +import { AddonModChatHelperProvider, AddonModChatMessageForView } from '../../providers/helper'; import { Network } from '@ionic-native/network'; -import * as moment from 'moment'; +import { coreSlideInOut } from '@classes/animations'; +import { CoreSendMessageFormComponent } from '@components/send-message-form/send-message-form'; /** * Page that displays a chat session. @@ -31,17 +32,20 @@ import * as moment from 'moment'; @Component({ selector: 'page-addon-mod-chat-chat', templateUrl: 'chat.html', + animations: [coreSlideInOut] }) export class AddonModChatChatPage { @ViewChild(Content) content: Content; + @ViewChild(CoreSendMessageFormComponent) sendMessageForm: CoreSendMessageFormComponent; loaded = false; title: string; - messages: AddonModChatMessageWithUserData[] = []; + messages: AddonModChatMessageForView[] = []; newMessage: string; polling: any; isOnline: boolean; - currentUserBeep: string; + currentUserId: number; + sending: boolean; protected logger; protected courseId: number; @@ -53,17 +57,18 @@ export class AddonModChatChatPage { protected keyboardObserver: any; protected viewDestroyed = false; protected pollingRunning = false; + protected users = []; constructor(navParams: NavParams, logger: CoreLoggerProvider, network: Network, zone: NgZone, private navCtrl: NavController, private chatProvider: AddonModChatProvider, private appProvider: CoreAppProvider, sitesProvider: CoreSitesProvider, - private modalCtrl: ModalController, private domUtils: CoreDomUtilsProvider, private textUtils: CoreTextUtilsProvider, - private eventsProvider: CoreEventsProvider) { + private modalCtrl: ModalController, private domUtils: CoreDomUtilsProvider, + private eventsProvider: CoreEventsProvider, private chatHelper: AddonModChatHelperProvider) { this.chatId = navParams.get('chatId'); this.courseId = navParams.get('courseId'); this.title = navParams.get('title'); this.logger = logger.getInstance('AddonModChoiceChoicePage'); - this.currentUserBeep = 'beep ' + sitesProvider.getCurrentSiteUserId(); + this.currentUserId = sitesProvider.getCurrentSiteUserId(); this.isOnline = this.appProvider.isOnline(); this.onlineObserver = network.onchange().subscribe(() => { // Execute the callback in the Angular zone, so change detection doesn't stop working. @@ -116,16 +121,62 @@ export class AddonModChatChatPage { * Display the chat users modal. */ showChatUsers(): void { - const modal = this.modalCtrl.create('AddonModChatUsersPage', {sessionId: this.sessionId}); + // Create the toc modal. + const modal = this.modalCtrl.create('AddonModChatUsersPage', { + sessionId: this.sessionId + }, { cssClass: 'core-modal-lateral', + showBackdrop: true, + enableBackdropDismiss: true, + enterAnimation: 'core-modal-lateral-transition', + leaveAnimation: 'core-modal-lateral-transition' }); + modal.onDidDismiss((data) => { if (data && data.talkTo) { - this.newMessage = `To ${data.talkTo}: `; + this.newMessage = `To ${data.talkTo}: ` + (this.sendMessageForm.message || ''); } if (data && data.beepTo) { this.sendMessage('', data.beepTo); } + if (data && data.users) { + this.users = data.users; + } + }); + + modal.present({ + ev: event + }); + } + + /** + * Get the user fullname for a beep. + * + * @param id User Id before parsing. + * @return User fullname. + */ + protected getUserFullname(id: string): Promise { + if (isNaN(parseInt(id, 10))) { + return Promise.resolve(id); + } + + const user = this.users.find((user) => user.id == id); + + if (user) { + return Promise.resolve(user.fullname); + } + + return this.chatProvider.getChatUsers(this.sessionId).then((data) => { + this.users = data.users; + const user = this.users.find((user) => user.id == id); + + if (user) { + return user.fullname; + } + + return id; + }).catch((error) => { + // Ignore errors. + return id; }); - modal.present(); } /** @@ -149,8 +200,26 @@ export class AddonModChatChatPage { this.lastTime = messagesInfo.chatnewlasttime || 0; return this.chatProvider.getMessagesUserData(messagesInfo.messages, this.courseId).then((messages) => { - this.messages = this.messages.concat( messages); if (messages.length) { + const previousLength = this.messages.length; + this.messages = this.messages.concat( messages); + + // Calculate which messages need to display the date or user data. + for (let index = previousLength ; index < this.messages.length; index++) { + const message = this.messages[index]; + const prevMessage = index > 0 ? this.messages[index - 1] : null; + + this.chatHelper.formatMessage(this.currentUserId, message, prevMessage); + + if (message.beep && message.beep != this.currentUserId + '') { + this.getUserFullname(message.beep).then((fullname) => { + message.beepWho = fullname; + }); + } + } + + this.messages[this.messages.length - 1].showTail = true; + // New messages or beeps, scroll to bottom. setTimeout(() => this.scrollToBottom()); } @@ -188,7 +257,7 @@ export class AddonModChatChatPage { /** * Convenience function to be called every certain time to fetch chat messages. * - * @return Promised resolved when done. + * @return Promise resolved when done. */ protected fetchMessagesInterval(): Promise { this.logger.debug('Polling for messages'); @@ -218,22 +287,6 @@ export class AddonModChatChatPage { }); } - /** - * Check if the date should be displayed between messages (when the day changes at midnight for example). - * - * @param message New message object. - * @param prevMessage Previous message object. - * @return True if messages are from diferent days, false othetwise. - */ - showDate(message: AddonModChatMessageWithUserData, prevMessage: AddonModChatMessageWithUserData): boolean { - if (!prevMessage) { - return true; - } - - // Check if day has changed. - return !moment(message.timestamp * 1000).isSame(prevMessage.timestamp * 1000, 'day'); - } - /** * Send a message to the chat. * @@ -248,9 +301,8 @@ export class AddonModChatChatPage { // Silent error. return; } - text = this.textUtils.replaceNewLines(text, '
'); - const modal = this.domUtils.showModalLoading('core.sending', true); + this.sending = true; this.chatProvider.sendMessage(this.sessionId, text, beep).then(() => { // Update messages to show the sent message. this.fetchMessagesInterval().catch(() => { @@ -261,9 +313,11 @@ export class AddonModChatChatPage { messages without the keyboard being closed. */ this.appProvider.closeKeyboard(); + this.newMessage = text; + this.domUtils.showErrorModalDefault(error, 'addon.mod_chat.errorwhilesendingmessage', true); }).finally(() => { - modal.dismiss(); + this.sending = false; }); } diff --git a/src/addon/mod/chat/pages/session-messages/session-messages.html b/src/addon/mod/chat/pages/session-messages/session-messages.html index a315ebecf..1f2a2ce14 100644 --- a/src/addon/mod/chat/pages/session-messages/session-messages.html +++ b/src/addon/mod/chat/pages/session-messages/session-messages.html @@ -8,33 +8,56 @@ -
-
- - {{ message.timestamp * 1000 | coreFormatDate:"strftimedayshort" }} - -
+ + +
+ {{ message.timestamp * 1000 | coreFormatDate:"strftimedayshort" }} +
-
- - {{ message.timestamp * 1000 | coreFormatDate:"strftimetime" }} {{ 'addon.mod_chat.messageenter' | translate:{$a: message.userfullname} }} - -
+
+ + {{ message.timestamp * 1000 | coreFormatDate:"strftimetime" }} {{ 'addon.mod_chat.messageenter' | translate:{$a: message.userfullname} }} + -
- - {{ message.timestamp * 1000 | coreFormatDate:"strftimetime" }} {{ 'addon.mod_chat.messageexit' | translate:{$a: message.userfullname} }} - -
+ + {{ message.timestamp * 1000 | coreFormatDate:"strftimetime" }} {{ 'addon.mod_chat.messageexit' | translate:{$a: message.userfullname} }} + - - -

-

{{ message.timestamp * 1000 | coreFormatDate:"strftimetime" }}

- -

- -
-
+ + {{ message.timestamp * 1000 | coreFormatDate:"strftimetime" }} + {{ 'addon.mod_chat.messagebeepseveryone' | translate:{$a: message.userfullname} }} + + + + {{ message.timestamp * 1000 | coreFormatDate:"strftimetime" }} + {{ 'addon.mod_chat.messagebeepsyou' | translate:{$a: message.userfullname} }} + + + + {{ message.timestamp * 1000 | coreFormatDate:"strftimetime" }} + {{ 'addon.mod_chat.messageyoubeep' | translate:{$a: message.beepWho} }} + + + + {{ message.timestamp * 1000 | coreFormatDate:"strftimetime" }} + {{ message.userfullname }} + +
+ + + +

+ +
{{ message.userfullname }}
+ {{ message.timestamp * 1000 | coreFormatDate: "strftimetime" }} +

+ +

+ +

+
+
+ +
diff --git a/src/addon/mod/chat/pages/session-messages/session-messages.scss b/src/addon/mod/chat/pages/session-messages/session-messages.scss index a8d1e96e8..ef01df6e2 100644 --- a/src/addon/mod/chat/pages/session-messages/session-messages.scss +++ b/src/addon/mod/chat/pages/session-messages/session-messages.scss @@ -1,9 +1,8 @@ -ion-app.app-root page-addon-mod-chat-session-messages { +ion-app.app-root page-addon-mod-chat-session-messages.ion-page { + @include message-page(); + .addon-mod-chat-notice { margin-top: 10px; margin-bottom: 10px; } - .addon-mod-chat-message { - align-items: flex-start; - } } diff --git a/src/addon/mod/chat/pages/session-messages/session-messages.ts b/src/addon/mod/chat/pages/session-messages/session-messages.ts index 1a29a8365..a70eda770 100644 --- a/src/addon/mod/chat/pages/session-messages/session-messages.ts +++ b/src/addon/mod/chat/pages/session-messages/session-messages.ts @@ -15,8 +15,10 @@ import { Component } from '@angular/core'; import { IonicPage, NavParams } from 'ionic-angular'; import { CoreDomUtilsProvider } from '@providers/utils/dom'; -import { AddonModChatProvider, AddonModChatSessionMessageWithUserData } from '../../providers/chat'; -import * as moment from 'moment'; +import { CoreSitesProvider } from '@providers/sites'; +import { CoreUserProvider } from '@core/user/providers/user'; +import { AddonModChatProvider } from '../../providers/chat'; +import { AddonModChatHelperProvider, AddonModChatSessionMessageForView } from '../../providers/helper'; /** * Page that displays list of chat session messages. @@ -28,20 +30,24 @@ import * as moment from 'moment'; }) export class AddonModChatSessionMessagesPage { + currentUserId: number; + protected courseId: number; protected chatId: number; protected sessionStart: number; protected sessionEnd: number; protected groupId: number; protected loaded = false; - protected messages: AddonModChatSessionMessageWithUserData[] = []; + protected messages: AddonModChatSessionMessageForView[] = []; - constructor(navParams: NavParams, private domUtils: CoreDomUtilsProvider, private chatProvider: AddonModChatProvider) { + constructor(navParams: NavParams, private domUtils: CoreDomUtilsProvider, private chatProvider: AddonModChatProvider, + sitesProvider: CoreSitesProvider, private chatHelper: AddonModChatHelperProvider, private userProvider: CoreUserProvider) { this.courseId = navParams.get('courseId'); this.chatId = navParams.get('chatId'); this.groupId = navParams.get('groupId'); this.sessionStart = navParams.get('sessionStart'); this.sessionEnd = navParams.get('sessionEnd'); + this.currentUserId = sitesProvider.getCurrentSiteUserId(); this.fetchMessages(); } @@ -55,7 +61,25 @@ export class AddonModChatSessionMessagesPage { return this.chatProvider.getSessionMessages(this.chatId, this.sessionStart, this.sessionEnd, this.groupId) .then((messages) => { return this.chatProvider.getMessagesUserData(messages, this.courseId).then((messages) => { - this.messages = messages; + this.messages = messages; + + if (messages.length) { + // Calculate which messages need to display the date or user data. + for (let index = 0 ; index < this.messages.length; index++) { + const message = this.messages[index]; + const prevMessage = index > 0 ? this.messages[index - 1] : null; + + this.chatHelper.formatMessage(this.currentUserId, message, prevMessage); + + if (message.beep && message.beep != this.currentUserId + '') { + this.getUserFullname(message.beep).then((fullname) => { + message.beepWho = fullname; + }); + } + } + + this.messages[this.messages.length - 1].showTail = true; + } }); }).catch((error) => { this.domUtils.showErrorModalDefault(error, 'core.errorloadingcontent', true); @@ -64,6 +88,25 @@ export class AddonModChatSessionMessagesPage { }); } + /** + * Get the user fullname for a beep. + * + * @param id User Id before parsing. + * @return User fullname. + */ + protected getUserFullname(id: string): Promise { + if (isNaN(parseInt(id, 10))) { + return Promise.resolve(id); + } + + return this.userProvider.getProfile(parseInt(id, 10), this.courseId, true).then((user) => { + return user.fullname; + }).catch(() => { + // Error getting profile. + return id; + }); + } + /** * Refresh session messages. * @@ -77,19 +120,4 @@ export class AddonModChatSessionMessagesPage { }); } - /** - * Check if the date should be displayed between messages (when the day changes at midnight for example). - * - * @param message New message object. - * @param prevMessage Previous message object. - * @return True if messages are from diferent days, false othetwise. - */ - showDate(message: AddonModChatSessionMessageWithUserData, prevMessage: AddonModChatSessionMessageWithUserData): boolean { - if (!prevMessage) { - return true; - } - - // Check if day has changed. - return !moment(message.timestamp * 1000).isSame(prevMessage.timestamp * 1000, 'day'); - } } diff --git a/src/addon/mod/chat/pages/users/users.ts b/src/addon/mod/chat/pages/users/users.ts index f54560ba3..3ec2b36e4 100644 --- a/src/addon/mod/chat/pages/users/users.ts +++ b/src/addon/mod/chat/pages/users/users.ts @@ -69,7 +69,7 @@ export class AddonModChatUsersPage { * Close the chat users modal. */ closeModal(): void { - this.viewCtrl.dismiss(); + this.viewCtrl.dismiss({users: this.users}); } /** @@ -77,8 +77,8 @@ export class AddonModChatUsersPage { * * @param user User object. */ - talkTo(user: AddonModChatUser): void { - this.viewCtrl.dismiss({talkTo: user.fullname}); + talkTo(user: AddonModChatUser): void { + this.viewCtrl.dismiss({talkTo: user.fullname, users: this.users}); } /** @@ -87,7 +87,7 @@ export class AddonModChatUsersPage { * @param user User object. */ beepTo(user: AddonModChatUser): void { - this.viewCtrl.dismiss({beepTo: user.id}); + this.viewCtrl.dismiss({beepTo: user.id, users: this.users}); } /** diff --git a/src/addon/mod/chat/providers/chat.ts b/src/addon/mod/chat/providers/chat.ts index f656375a7..45a102a06 100644 --- a/src/addon/mod/chat/providers/chat.ts +++ b/src/addon/mod/chat/providers/chat.ts @@ -20,6 +20,7 @@ import { CoreCourseLogHelperProvider } from '@core/course/providers/log-helper'; import { CoreUtilsProvider } from '@providers/utils/utils'; import { CoreSite, CoreSiteWSPreSets } from '@classes/site'; import { CoreWSExternalWarning, CoreWSExternalFile } from '@providers/ws'; +import { AddonModChatMessageForView, AddonModChatSessionMessageForView } from './helper'; /** * Service that provides some features for chats. @@ -157,9 +158,9 @@ export class AddonModChatProvider { * @return Promise always resolved with the formatted messages. */ getMessagesUserData(messages: (AddonModChatMessage | AddonModChatSessionMessage)[], courseId: number) - : Promise<(AddonModChatMessageWithUserData | AddonModChatSessionMessageWithUserData)[]> { + : Promise<(AddonModChatMessageForView | AddonModChatSessionMessageForView)[]> { - const promises = messages.map((message: AddonModChatMessageWithUserData | AddonModChatSessionMessageWithUserData) => { + const promises = messages.map((message: AddonModChatMessageForView | AddonModChatSessionMessageForView) => { return this.userProvider.getProfile(message.userid, courseId, true).then((user) => { message.userfullname = user.fullname; message.userprofileimageurl = user.profileimageurl; @@ -448,7 +449,7 @@ export type AddonModChatMessage = { }; /** - * Message with user data + * Message with user data. */ export type AddonModChatMessageWithUserData = AddonModChatMessage & AddonModChatMessageUserData; @@ -484,7 +485,7 @@ export type AddonModChatSessionMessage = { }; /** - * Message with user data + * Session message with user data. */ export type AddonModChatSessionMessageWithUserData = AddonModChatSessionMessage & AddonModChatMessageUserData; diff --git a/src/addon/mod/chat/providers/helper.ts b/src/addon/mod/chat/providers/helper.ts new file mode 100644 index 000000000..c575b64a7 --- /dev/null +++ b/src/addon/mod/chat/providers/helper.ts @@ -0,0 +1,132 @@ +// (C) Copyright 2015 Martin Dougiamas +// +// 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 { Injectable } from '@angular/core'; +import { TranslateService } from '@ngx-translate/core'; +import { CoreTextUtilsProvider } from '@providers/utils/text'; +import * as moment from 'moment'; +import { AddonModChatMessageWithUserData, AddonModChatSessionMessageWithUserData } from './chat'; + +/** + * Helper service that provides some features for chat. + */ +@Injectable() +export class AddonModChatHelperProvider { + + static patternto = new RegExp(/^To\s([^:]+):(.*)/); + + constructor(protected translate: TranslateService, + protected textUtils: CoreTextUtilsProvider) { + + } + + /** + * Give some format info about messages. + * + * @param currentUserId User Id. + * @param message Message in a discussion. + * @param prevMessage Previous Message in a discussion (if any). + * @return Message with additional info. + */ + formatMessage(currentUserId: number, message: AddonModChatMessageForView | AddonModChatSessionMessageForView, + prevMessage?: AddonModChatMessageForView | AddonModChatSessionMessageForView): any { + message.message = message.message.trim(); + + message.showDate = this.showDate(message, prevMessage); + message.beep = message.message.substr(0, 5) == 'beep ' && message.message.substr(5).trim(); + + message.special = ( message).issystem || ( message).system || + !!message.beep; + + if (message.message.substr(0, 4) == '/me ') { + message.special = true; + message.message = message.message.substr(4).trim(); + } + + if (!message.special && message.message.match(AddonModChatHelperProvider.patternto)) { + const matches = message.message.match(AddonModChatHelperProvider.patternto); + message.message = '' + this.translate.instant('addon.mod_chat.saidto') + + ' ' + matches[1] + ': ' + matches[2]; + } + + message.showUserData = this.showUserData(currentUserId, message, prevMessage); + prevMessage ? + prevMessage.showTail = this.showTail(prevMessage, message) : null; + } + + /** + * Check if the user info should be displayed for the current message. + * User data is only displayed if the previous message was from another user. + * + * @param message Current message where to show the user info. + * @param prevMessage Previous message. + * @return Whether user data should be shown. + */ + protected showUserData(currentUserId: number, message: AddonModChatMessageForView | AddonModChatSessionMessageForView, + prevMessage?: AddonModChatMessageForView | AddonModChatSessionMessageForView): boolean { + return message.userid != currentUserId && + (!prevMessage || prevMessage.userid != message.userid || message.showDate || prevMessage.special); + } + + /** + * Check if a css tail should be shown. + * + * @param message Current message where to show the user info. + * @param nextMessage Next message. + * @return Whether user data should be shown. + */ + protected showTail(message: AddonModChatMessageForView | AddonModChatSessionMessageForView, + nextMessage?: AddonModChatMessageForView | AddonModChatSessionMessageForView): boolean { + return !nextMessage || nextMessage.userid != message.userid || nextMessage.showDate || nextMessage.special; + } + + /** + * Check if the date should be displayed between messages (when the day changes at midnight for example). + * + * @param message New message object. + * @param prevMessage Previous message object. + * @return True if messages are from diferent days, false othetwise. + */ + protected showDate(message: AddonModChatMessageForView | AddonModChatSessionMessageForView, + prevMessage: AddonModChatMessageForView | AddonModChatSessionMessageForView): boolean { + if (!prevMessage) { + return true; + } + + // Check if day has changed. + return !moment(message.timestamp * 1000).isSame(prevMessage.timestamp * 1000, 'day'); + } +} + +/** + * Special info for view usage. + */ +type AddonModChatInfoForView = { + showDate?: boolean; // If date should be displayed before the message. + beep?: string; // User id of the beeped user or 'all'. + special?: boolean; // True if is an special message (system, beep or command). + showUserData?: boolean; // If user data should be displayed. + showTail?: boolean; // If tail should be displayed (decoration). + beepWho?: string; // Fullname of the beeped user. +}; + +/** + * Message with data for view usage. + */ +export type AddonModChatMessageForView = AddonModChatMessageWithUserData & AddonModChatInfoForView; + +/** + * Session message with data for view usage. + */ +export type AddonModChatSessionMessageForView = AddonModChatSessionMessageWithUserData & AddonModChatInfoForView; diff --git a/src/assets/lang/en.json b/src/assets/lang/en.json index d4514cc94..58ceb5175 100644 --- a/src/assets/lang/en.json +++ b/src/assets/lang/en.json @@ -432,14 +432,17 @@ "addon.mod_chat.errorwhilegettingchatusers": "Error while getting chat users.", "addon.mod_chat.errorwhileretrievingmessages": "Error while retrieving messages from the server.", "addon.mod_chat.errorwhilesendingmessage": "Error while sending the message.", + "addon.mod_chat.messagebeepseveryone": "{{$a}} beeps everyone!", "addon.mod_chat.messagebeepsyou": "{{$a}} has just beeped you!", "addon.mod_chat.messageenter": "{{$a}} has just entered this chat", "addon.mod_chat.messageexit": "{{$a}} has left this chat", "addon.mod_chat.messages": "Messages", + "addon.mod_chat.messageyoubeep": "You beeped {{$a}}", "addon.mod_chat.modulenameplural": "Chats", "addon.mod_chat.mustbeonlinetosendmessages": "You must be online to send messages.", "addon.mod_chat.nomessages": "No messages yet", "addon.mod_chat.nosessionsfound": "No sessions found", + "addon.mod_chat.saidto": "said to", "addon.mod_chat.send": "Send", "addon.mod_chat.sessionstart": "The next chat session will start on {{$a.date}}, ({{$a.fromnow}} from now)", "addon.mod_chat.showincompletesessions": "Show incomplete sessions", diff --git a/src/components/ion-tabs/ion-tabs.scss b/src/components/ion-tabs/ion-tabs.scss index 31f667650..ec2e85b66 100644 --- a/src/components/ion-tabs/ion-tabs.scss +++ b/src/components/ion-tabs/ion-tabs.scss @@ -60,7 +60,8 @@ ion-app.app-root core-ion-tabs { } } - .scroll-content, .fixed-content { + ion-content:not(.has-footer) > .scroll-content, + ion-content:not(.has-footer) > .fixed-content { margin-bottom: 0 !important; } } diff --git a/src/components/send-message-form/core-send-message-form.html b/src/components/send-message-form/core-send-message-form.html index b1829854a..8caa886c3 100644 --- a/src/components/send-message-form/core-send-message-form.html +++ b/src/components/send-message-form/core-send-message-form.html @@ -1,7 +1,7 @@
- diff --git a/src/components/send-message-form/send-message-form.ts b/src/components/send-message-form/send-message-form.ts index 48a7a87c6..7076f0d4f 100644 --- a/src/components/send-message-form/send-message-form.ts +++ b/src/components/send-message-form/send-message-form.ts @@ -39,6 +39,7 @@ export class CoreSendMessageFormComponent implements OnInit { @Input() message: string; // Input text. @Input() placeholder = ''; // Placeholder for the input area. @Input() showKeyboard = false; // If keyboard is shown or not. + @Input() sendDisabled = false; // If send is disabled. @Output() onSubmit: EventEmitter; // Send data when submitting the message form. @Output() onResize: EventEmitter; // Emit when resizing the textarea. @@ -99,6 +100,10 @@ export class CoreSendMessageFormComponent implements OnInit { * @param other The name of the other key that was clicked, undefined if no other key. */ enterClicked(e: Event, other: string): void { + if (this.sendDisabled) { + return; + } + if (this.sendOnEnter && !other) { // Enter clicked, send the message. this.submitForm(e); diff --git a/src/theme/dark.scss b/src/theme/dark.scss index 27800e723..fe18c049a 100644 --- a/src/theme/dark.scss +++ b/src/theme/dark.scss @@ -21,7 +21,7 @@ ion-app.app-root .ion-page { color: $core-dark-text-color; background-color: $core-dark-item-bg-color; - a { + a:not(.button) { color: $core-dark-link-color; }