Step 2: Creating page Messages: userlist last messages + a page for all the messages received and sent.

This commit is contained in:
Paolo Arena
2019-10-27 00:37:00 +02:00
parent 878ae96813
commit 4f895bdbe2
19 changed files with 407 additions and 159 deletions

View File

@@ -6,8 +6,13 @@ export const shared_consts = {
Manager: 2, Manager: 2,
}, },
MessageOptions: {
Notify_ByEmail: 2,
Notify_ByPushNotification: 4
},
fieldsUserToChange() { fieldsUserToChange() {
return ['username', 'email', 'name', 'surname', 'perm', 'date_reg', 'verified_email', 'img', 'ipaddr'] return ['username', 'email', 'name', 'surname', 'perm', 'date_reg', 'verified_email', 'img', 'ipaddr', 'lasttimeonline']
} }
} }

View File

@@ -28,10 +28,12 @@ import router from '@router'
import { static_data } from '@src/db/static_data' import { static_data } from '@src/db/static_data'
import translate from '@src/globalroutines/util' import translate from '@src/globalroutines/util'
import { lists } from '../../store/Modules/lists' import { lists } from '../../store/Modules/lists'
import { GlobalStore } from '../../store/Modules' import { GlobalStore, MessageStore } from '../../store/Modules'
import { IMessagePage, IMessage, IIdentity } from '../../model' import { IMessagePage, IMessage, IIdentity, MsgDefault } from '../../model'
import MixinUsers from '../../mixins/mixin-users'
@Component({ @Component({
mixins: [MixinUsers],
name: 'CEventsCalendar', name: 'CEventsCalendar',
components: { Logo, Footer, CTitle, CImgText, QDateTimeScroller, QDateScroller, CMySelect, CMyEditor } components: { Logo, Footer, CTitle, CImgText, QDateTimeScroller, QDateScroller, CMySelect, CMyEditor }
}) })
@@ -370,10 +372,6 @@ export default class CEventsCalendar extends Vue {
} }
} }
get mythis() {
return this
}
public $refs: { public $refs: {
calendar: any calendar: any
} }
@@ -666,26 +664,17 @@ export default class CEventsCalendar extends Vue {
const data: IMessage = { const data: IMessage = {
source: { source: {
page: '',
event_id: myevent._id, event_id: myevent._id,
infoevent: tools.gettextevent(this, myevent) infoevent: tools.gettextevent(this, myevent)
}, },
idapp: process.env.APP_ID,
origin: {
idapp: process.env.APP_ID,
username: UserStore.state.username
},
dest: { dest: {
idapp: process.env.APP_ID, idapp: process.env.APP_ID,
username: myevent.teacher username: myevent.teacher
}, },
read: false, message: this.askInfoForm.message
deleted: false,
message: this.askInfoForm.message,
datemsg: tools.getDateNow()
} }
this.SendMsgEvent(data).then((ris) => { MessageStore.actions.SendMsgEvent(data).then((ris) => {
self.contextDay = null self.contextDay = null
if (ris) if (ris)
tools.showPositiveNotif(self.$q, self.$t('cal.sendmsg_sent')) tools.showPositiveNotif(self.$q, self.$t('cal.sendmsg_sent'))
@@ -841,10 +830,6 @@ export default class CEventsCalendar extends Vue {
return await CalendarStore.actions.BookEvent(eventparam) return await CalendarStore.actions.BookEvent(eventparam)
} }
public async SendMsgEvent(param: IMessage) {
return await UserStore.actions.SendMsgEvent(param)
}
public isAlreadyBooked(eventparam: IEvents) { public isAlreadyBooked(eventparam: IEvents) {
return CalendarStore.getters.findEventBooked(eventparam, true) return CalendarStore.getters.findEventBooked(eventparam, true)
} }
@@ -880,7 +865,7 @@ export default class CEventsCalendar extends Vue {
public createContribType(value) { public createContribType(value) {
console.log('createContribType', value) console.log('createContribType', value)
tools.createNewRecord(this, 'contribtype', { label: value }).then((myrec) => { tools.createNewRecord(this, 'contribtype', { label: value }).then((myrec) => {
console.log('myrec') // console.log('myrec')
CalendarStore.state.contribtype.push(myrec) CalendarStore.state.contribtype.push(myrec)
}) })
} }
@@ -901,10 +886,6 @@ export default class CEventsCalendar extends Vue {
return myprice return myprice
} }
public getTeacherName(teacherusername) {
return CalendarStore.getters.getTeacherName(teacherusername)
}
public getWhereIcon(where) { public getWhereIcon(where) {
const whererec = CalendarStore.getters.getWhereRec(where) const whererec = CalendarStore.getters.getWhereRec(where)
return (whererec) ? whererec.whereicon : '' return (whererec) ? whererec.whereicon : ''
@@ -915,11 +896,6 @@ export default class CEventsCalendar extends Vue {
return (whererec) ? whererec.placename : '' return (whererec) ? whererec.placename : ''
} }
public getTeacherImg(teacherusername) {
const teacher = CalendarStore.getters.getTeacher(teacherusername)
return (teacher) ? teacher.img : ''
}
public badgeClasses(eventparam, type) { public badgeClasses(eventparam, type) {
const cssColor = tools.isCssColor(eventparam.bgcolor) const cssColor = tools.isCssColor(eventparam.bgcolor)
const isHeader = type === 'header' const isHeader = type === 'header'

View File

@@ -39,17 +39,17 @@
<span class="cal__teacher-content"> <span class="cal__teacher-content">
<q-chip> <q-chip>
<q-avatar> <q-avatar>
<img :src="`../../statics/images/` + getTeacherImg(myevent.teacher)"> <img :src="`../../statics/images/` + getImgByUsername(myevent.teacher)">
</q-avatar> </q-avatar>
<span class="cal__teacher-content">{{getTeacherName(myevent.teacher)}}</span> <span class="cal__teacher-content">{{getUserByUsername(myevent.teacher)}}</span>
</q-chip> </q-chip>
<span v-if="getTeacherImg(myevent.teacher2) && myevent.teacher2" <span v-if="getImgByUsername(myevent.teacher2) && myevent.teacher2"
class="margin_avatar2"></span> class="margin_avatar2"></span>
<q-chip v-if="getTeacherImg(myevent.teacher2) && myevent.teacher2"> <q-chip v-if="getImgByUsername(myevent.teacher2) && myevent.teacher2">
<q-avatar> <q-avatar>
<img :src="`../../statics/images/` + getTeacherImg(myevent.teacher2)"> <img :src="`../../statics/images/` + getImgByUsername(myevent.teacher2)">
</q-avatar> </q-avatar>
<span class="cal__teacher-content">{{getTeacherName(myevent.teacher2)}}</span> <span class="cal__teacher-content">{{getUserByUsername(myevent.teacher2)}}</span>
</q-chip> </q-chip>
</span> </span>
</div> </div>
@@ -75,7 +75,7 @@
</div> </div>
<div v-if="myevent.dateTimeStart" class="cal__when"> <div v-if="myevent.dateTimeStart" class="cal__when">
<span class="cal__where-title">{{$t('cal.when')}}: <span class="cal__where-title">{{$t('cal.when')}}:
<span v-html="tools.getstrDateTimeEvent(mythis, myevent, true)"></span> <span v-html="tools.getstrDateTimeEvent(mythis(), myevent, true)"></span>
</span> </span>
</div> </div>
<p v-if="myevent.linkpdf" style="margin-top: 10px; text-align: center"> <p v-if="myevent.linkpdf" style="margin-top: 10px; text-align: center">
@@ -294,7 +294,7 @@
</q-chip> </q-chip>
<div v-if="myevent.dateTimeStart" class="cal__when"> <div v-if="myevent.dateTimeStart" class="cal__when">
<span class="cal__where-title">{{$t('cal.when')}}: <span class="cal__where-title">{{$t('cal.when')}}:
<span v-html="tools.getstrDateTimeEvent(mythis, myevent, true)"></span> <span v-html="tools.getstrDateTimeEvent(mythis(), myevent, true)"></span>
</span> </span>
</div> </div>
<div class="q-pa-xs"> <div class="q-pa-xs">
@@ -334,7 +334,7 @@
<q-card-actions align="right"> <q-card-actions align="right">
<q-btn v-if="bookEventpage.state === EState.Modifying" flat :label="$t('cal.cancelbooking')" <q-btn v-if="bookEventpage.state === EState.Modifying" flat :label="$t('cal.cancelbooking')"
color="negative" color="negative"
@click="tools.CancelBookingEvent(mythis, myevent, bookEventForm._id, true)"></q-btn> @click="tools.CancelBookingEvent(mythis(), myevent, bookEventForm._id, true)"></q-btn>
<q-btn v-if="checkseinviaMsg" flat :label="$t('dialog.sendonlymsg')" color="primary" <q-btn v-if="checkseinviaMsg" flat :label="$t('dialog.sendonlymsg')" color="primary"
@click="sendMsg(myevent)"></q-btn> @click="sendMsg(myevent)"></q-btn>
<q-btn v-else flat :label="getTitleBtnBooking" color="primary" @click="saveBookEvent(myevent)" <q-btn v-else flat :label="getTitleBtnBooking" color="primary" @click="saveBookEvent(myevent)"
@@ -370,7 +370,7 @@
</q-chip> </q-chip>
<div v-if="myevent.dateTimeStart" class="cal__when"> <div v-if="myevent.dateTimeStart" class="cal__when">
<span class="cal__where-title">{{$t('cal.when')}}: <span class="cal__where-title">{{$t('cal.when')}}:
<span v-html="tools.getstrDateTimeEvent(mythis, myevent, true)"></span> <span v-html="tools.getstrDateTimeEvent(mythis(), myevent, true)"></span>
</span> </span>
</div> </div>
<div class="q-pa-xs"> <div class="q-pa-xs">
@@ -560,7 +560,7 @@
</p> </p>
<div class="listaev__date listaev__align_center_mobile"> <div class="listaev__date listaev__align_center_mobile">
<span v-html="tools.getstrDateTimeEvent(mythis, event, true)"></span> <span v-html="tools.getstrDateTimeEvent(mythis(), event, true)"></span>
</div> </div>
<div class="listaev__align_center_mobile"> <div class="listaev__align_center_mobile">
@@ -600,16 +600,16 @@
<q-chip> <q-chip>
<q-avatar> <q-avatar>
<img :src="`../../statics/images/` + getTeacherImg(event.teacher)"> <img :src="`../../statics/images/` + getImgByUsername(event.teacher)">
</q-avatar> </q-avatar>
<span class="cal__teacher-content">{{getTeacherName(event.teacher)}}</span> <span class="cal__teacher-content">{{getUserByUsername(event.teacher)}}</span>
</q-chip> </q-chip>
<span v-if="getTeacherImg(event.teacher2)" class="margin_avatar2"></span> <span v-if="getImgByUsername(event.teacher2)" class="margin_avatar2"></span>
<q-chip v-if="getTeacherImg(event.teacher2) && event.teacher2"> <q-chip v-if="getImgByUsername(event.teacher2) && event.teacher2">
<q-avatar> <q-avatar>
<img :src="`../../statics/images/` + getTeacherImg(event.teacher2)"> <img :src="`../../statics/images/` + getImgByUsername(event.teacher2)">
</q-avatar> </q-avatar>
<span class="cal__teacher-content">{{getTeacherName(event.teacher2)}}</span> <span class="cal__teacher-content">{{getUserByUsername(event.teacher2)}}</span>
</q-chip> </q-chip>
<span v-if="event.wherecode" class=""> <span v-if="event.wherecode" class="">

View File

@@ -39,7 +39,7 @@
:label="$t('grid.editvalues')"></q-toggle> :label="$t('grid.editvalues')"></q-toggle>
<q-btn v-if="mytable" label="Refresh" color="primary" @click="refresh" class="q-mx-sm"></q-btn> <q-btn v-if="mytable" label="Refresh" color="primary" @click="refresh" class="q-mx-sm"></q-btn>
<q-btn v-if="mytable" flat dense color="primary" :disable="loading" label="Add Record" <q-btn v-if="mytable" flat dense color="primary" :disable="loading || !canEdit" :label="$t('grid.addrecord')"
@click="createNewRecord"></q-btn> @click="createNewRecord"></q-btn>
<q-space/> <q-space/>
@@ -77,7 +77,7 @@
<q-inner-loading :showing="spinner_visible"> <q-inner-loading :showing="spinner_visible">
<q-spinner-gears size="50px" color="primary"/> <q-spinner-tail size="2em" color="primary"/>
</q-inner-loading> </q-inner-loading>
</template> </template>

View File

@@ -39,7 +39,7 @@ export default class CMySelect extends Vue {
public mounted() { public mounted() {
const rec = this.options.find((myrec) => myrec[`${this.optval}`] === this.value) const rec = this.options.find((myrec) => myrec[`${this.optval}`] === this.value)
console.log('rec', rec) // console.log('rec', rec)
if (!this.useinput) { if (!this.useinput) {
this.myvalue = this.value this.myvalue = this.value
} else { } else {
@@ -49,7 +49,7 @@ export default class CMySelect extends Vue {
else else
this.myvalue = rec[`${this.optlab}`] this.myvalue = rec[`${this.optlab}`]
console.log('this.myvalue', this.myvalue, 'this.optval', this.optval, 'rec', rec[`${this.optval}`]) // console.log('this.myvalue', this.myvalue, 'this.optval', this.optval, 'rec', rec[`${this.optval}`])
} }
} }
} }

View File

@@ -9,6 +9,7 @@
v-model.trim="precDescr" v-model.trim="precDescr"
autogrow autogrow
borderless borderless
debounce="1000"
:label="getlabeltext" :label="getlabeltext"
dense dense
@focus="getFocus($event)" @focus="getFocus($event)"

View File

@@ -54,6 +54,7 @@
<q-input v-if="viewtaskTop" ref="insertTask" color="blue-12" v-model="todotop" :label="$t('todo.inserttop')" <q-input v-if="viewtaskTop" ref="insertTask" color="blue-12" v-model="todotop" :label="$t('todo.inserttop')"
style="margin-left: 6px;" style="margin-left: 6px;"
debounce="1000"
:after="[{icon: 'arrow_forward', content: true, handler () {}}]" :after="[{icon: 'arrow_forward', content: true, handler () {}}]"
v-on:keyup.enter="dbInsert(true)"> v-on:keyup.enter="dbInsert(true)">
<template v-slot:prepend> <template v-slot:prepend>
@@ -116,9 +117,12 @@
<q-input v-if="(TodosCount > 0 || !viewtaskTop) && CanIModifyTodo" ref="insertTaskBottom" v-model="todobottom" <q-input v-if="(TodosCount > 0 || !viewtaskTop) && CanIModifyTodo" ref="insertTaskBottom" v-model="todobottom"
style="margin-left: 6px;" style="margin-left: 6px;"
color="blue-12" color="blue-12"
debounce="1000"
:label="$t('todo.insertbottom')" :label="$t('todo.insertbottom')"
:after="[{icon: 'arrow_forward', content: true, handler () {}}]" :after="[{icon: 'arrow_forward', content: true, handler () {}}]"
v-on:keyup.enter="dbInsert(false)"/> v-on:keyup.enter="dbInsert(false)">
</q-input>
<br> <br>

View File

@@ -13,6 +13,7 @@
<div class="flex-item donotdrag divdescrTot"> <div class="flex-item donotdrag divdescrTot">
<q-input v-if="sel && inEdit && itemtodo.statustodo !== tools.Status.COMPLETED" hide-underline type="textarea" ref="inputdescr" <q-input v-if="sel && inEdit && itemtodo.statustodo !== tools.Status.COMPLETED" hide-underline type="textarea" ref="inputdescr"
v-model.trim="precDescr" v-model.trim="precDescr"
debounce="1000"
autogrow autogrow
borderless borderless
:readonly="!CanIModifyTodo" :readonly="!CanIModifyTodo"

View File

@@ -1,7 +1,7 @@
<template> <template>
<div> <div>
<q-btn flat round dense icon="fas fa-comment" class="q-mx-xs" > <q-btn flat round dense icon="fas fa-comment" class="q-mx-xs" >
<q-badge floating color="red">{{getNumMsgUnread}}</q-badge> <q-badge v-if="getNumMsgUnread > 0" floating color="red">{{getNumMsgUnread}}</q-badge>
<q-menu self="top right"> <q-menu self="top right">
<q-list bordered class="rounded-borders" style="max-width: 350px; min-width: 250px;"> <q-list bordered class="rounded-borders" style="max-width: 350px; min-width: 250px;">
<q-item-label header>{{$t('msgs.messages')}}</q-item-label> <q-item-label header>{{$t('msgs.messages')}}</q-item-label>
@@ -19,14 +19,14 @@
<q-item-section avatar> <q-item-section avatar>
<q-avatar> <q-avatar>
<img :src="getImgByUsername(msg.dest.username)"> <img :src="getImgByMsg(msg)">
</q-avatar> </q-avatar>
</q-item-section> </q-item-section>
<q-item-section> <q-item-section>
<q-item-label lines="1">{{getUserByUsername(msg.dest.username)}}</q-item-label> <q-item-label lines="1">{{getUsernameChatByMsg(msg)}}</q-item-label>
<q-item-label caption lines="2"> <q-item-label caption lines="2">
{{msg.message}} {{getMsgText(msg, false)}}
</q-item-label> </q-item-label>
</q-item-section> </q-item-section>

View File

@@ -5,6 +5,7 @@ import { GlobalStore, UserStore, MessageStore } from '../store/Modules'
import Component from 'vue-class-component' import Component from 'vue-class-component'
import { func_tools } from '../store/Modules/toolsext' import { func_tools } from '../store/Modules/toolsext'
import { tools } from '../store/Modules/tools' import { tools } from '../store/Modules/tools'
import { IMessage } from '@src/model'
// You can declare a mixin as the same style as components. // You can declare a mixin as the same style as components.
@Component @Component
@@ -12,43 +13,71 @@ export default class MixinUsers extends Vue {
public mythis() { public mythis() {
return this return this
} }
get func_tools() { get func_tools() {
return func_tools return func_tools
} }
get tools() { get tools() {
return tools return tools
} }
public getUserByUsername(username) { public getUserByUsername(username) {
return UserStore.getters.getNameSurnameByUsername(username) return UserStore.getters.getNameSurnameByUsername(username)
} }
public getImgByUsername(username) { public getImgByUsername(username) {
return `statics/` + UserStore.getters.getImgByUsername(username) return `statics/` + UserStore.getters.getImgByUsername(username)
} }
public getMyUsername() { public getMyUsername() {
return UserStore.state.my.username return UserStore.state.my.username
} }
public getUsernameChatByMsg(msg: IMessage) {
if (msg) {
if (msg.dest.username !== this.getMyUsername())
return msg.dest.username
else
return msg.origin.username
} else {
return ''
}
}
public getImgByMsg(msg: IMessage) {
return `statics/` + UserStore.getters.getImgByUsername(this.getUsernameChatByMsg(msg))
}
get getMyImg() { get getMyImg() {
return 'statics/' + UserStore.getters.getImgByUsername(UserStore.state.my.username) return 'statics/' + UserStore.getters.getImgByUsername(UserStore.state.my.username)
} }
get MenuCollapse() { get MenuCollapse() {
return GlobalStore.state.menuCollapse return GlobalStore.state.menuCollapse
// return true // return true
} }
get Username() { get Username() {
return UserStore.state.my.username return UserStore.state.my.username
} }
get myName() { get myName() {
return UserStore.state.my.name return UserStore.state.my.name
} }
get mySurname() { get mySurname() {
return UserStore.state.my.surname return UserStore.state.my.surname
} }
get Verificato() { get Verificato() {
return UserStore.state.my.verified_email return UserStore.state.my.verified_email
} }
get Email() { get Email() {
return UserStore.state.my.email return UserStore.state.my.email
} }
get getNumMsg() { get getNumMsg() {
return MessageStore.getters.getlasts_messages().length return MessageStore.getters.getlasts_messages().length
} }
@@ -58,4 +87,17 @@ export default class MixinUsers extends Vue {
return MessageStore.getters.getnumMsgUnread() return MessageStore.getters.getnumMsgUnread()
} }
public getMsgText(msg: IMessage, inarray: boolean) {
let add = ''
if (msg.origin.username === this.getMyUsername())
add = 'Tu: '
const ris = add + msg.message
if (inarray)
return [ris]
else
return ris
}
} }

View File

@@ -43,9 +43,6 @@ export interface IBookedEvent {
export interface IOperators { export interface IOperators {
username: string username: string
name: string
surname: string
email: string
cell: string cell: string
webpage?: string webpage?: string
img: string img: string

View File

@@ -20,6 +20,7 @@ export interface IUserFields {
img?: string img?: string
verified_email?: boolean verified_email?: boolean
tokens?: IToken[] tokens?: IToken[]
lasttimeonline?: Date
} }
/* /*

View File

@@ -4,6 +4,7 @@ const msgglobal = {
it: { it: {
grid: { grid: {
editvalues: 'Modifica Valori', editvalues: 'Modifica Valori',
addrecord: 'Aggiungi Riga',
showprevedit: 'Mostra Eventi Passati', showprevedit: 'Mostra Eventi Passati',
columns: 'Colonne', columns: 'Colonne',
tableslist: 'Tabelle', tableslist: 'Tabelle',
@@ -289,6 +290,7 @@ const msgglobal = {
es: { es: {
grid: { grid: {
editvalues: 'Cambiar valores', editvalues: 'Cambiar valores',
addrecord: 'Agregar fila',
showprevedit: 'Mostrar eventos pasados', showprevedit: 'Mostrar eventos pasados',
columns: 'Columnas', columns: 'Columnas',
tableslist: 'Tablas' tableslist: 'Tablas'
@@ -565,6 +567,7 @@ const msgglobal = {
fr: { fr: {
grid: { grid: {
editvalues: 'Changer les valeurs', editvalues: 'Changer les valeurs',
addrecord: 'Ajouter une ligne',
showprevedit: 'Afficher les événements passés', showprevedit: 'Afficher les événements passés',
columns: 'Colonnes', columns: 'Colonnes',
tableslist: 'Tables', tableslist: 'Tables',
@@ -840,6 +843,7 @@ const msgglobal = {
enUs: { enUs: {
grid: { grid: {
editvalues: 'Edit Values', editvalues: 'Edit Values',
addrecord: 'Add Row',
showprevedit: 'Show Past Events', showprevedit: 'Show Past Events',
columns: 'Columns', columns: 'Columns',
tableslist: 'Tables', tableslist: 'Tables',
@@ -1114,6 +1118,7 @@ const msgglobal = {
de: { de: {
grid: { grid: {
editvalues: 'Edit Values', editvalues: 'Edit Values',
addrecord: 'Add Row',
showprevedit: 'Show Past Events', showprevedit: 'Show Past Events',
columns: 'Columns', columns: 'Columns',
tableslist: 'Tables', tableslist: 'Tables',

View File

@@ -65,18 +65,6 @@ namespace Getters {
return mystate.bookedevent.filter((bookedevent) => (bookedevent.id_bookedevent === idevent) && (bookedevent.booked) && (showall || (!showall && bookedevent.userId === UserStore.state.my._id) )) return mystate.bookedevent.filter((bookedevent) => (bookedevent.id_bookedevent === idevent) && (bookedevent.booked) && (showall || (!showall && bookedevent.userId === UserStore.state.my._id) ))
}, 'getEventsBookedByIdEvent') }, 'getEventsBookedByIdEvent')
const getTeacherName = b.read((mystate: ICalendarState) => (teacherusername) => {
const op = mystate.operators.find((myop) => myop.username === teacherusername)
return (op) ? `${op.name} ${op.surname}` : ''
}, 'getTeacherName')
const getTeacher = b.read((mystate: ICalendarState) => (teacherusername) => {
const op = mystate.operators.find((myop) => myop.username === teacherusername)
return (op)
}, 'getTeacher')
const getWhereRec = b.read((mystate: ICalendarState) => (wherecode) => { const getWhereRec = b.read((mystate: ICalendarState) => (wherecode) => {
const whererec = mystate.wheres.find((mywhere) => mywhere.code === wherecode) const whererec = mystate.wheres.find((mywhere) => mywhere.code === wherecode)
return (whererec) return (whererec)
@@ -110,9 +98,6 @@ namespace Getters {
get getEventsBookedByIdEvent() { get getEventsBookedByIdEvent() {
return getEventsBookedByIdEvent() return getEventsBookedByIdEvent()
}, },
get getTeacher() {
return getTeacher()
},
get getWhereRec() { get getWhereRec() {
return getWhereRec() return getWhereRec()
}, },
@@ -124,12 +109,8 @@ namespace Getters {
}, },
get getContribtypeRecByLabel() { get getContribtypeRecByLabel() {
return getContribtypeRecByLabel() return getContribtypeRecByLabel()
},
get getTeacherName() {
return getTeacherName()
} }
} }
} }
namespace Mutations { namespace Mutations {

View File

@@ -138,9 +138,11 @@ namespace Getters {
}, 'getUserByUsername') }, 'getUserByUsername')
const getImgByUsername = b.read((mystate: IUserState) => (username): string => { const getImgByUsername = b.read((mystate: IUserState) => (username): string => {
if (username === '')
return 'images/avatar/avatar3_small.png'
// Check if is this User! // Check if is this User!
const myrec = UserStore.getters.getUserByUsername(username) const myrec = UserStore.getters.getUserByUsername(username)
if (myrec && !!myrec.img) { if (myrec && !!myrec.img && myrec.img !== '' && myrec.img !== 'undefined') {
return myrec.img return myrec.img
} else { } else {
return 'images/avatar/avatar3_small.png' return 'images/avatar/avatar3_small.png'

View File

@@ -27,6 +27,7 @@ import { serv_constants } from '@src/store/Modules/serv_constants'
import { shared_consts } from '@src/common/shared_vuejs' import { shared_consts } from '@src/common/shared_vuejs'
import { dom } from 'quasar' import { dom } from 'quasar'
const { height, width } = dom const { height, width } = dom
export interface INotify { export interface INotify {
@@ -2470,12 +2471,41 @@ export const tools = {
// return height() // return height()
return mythis.$q.screen.height return mythis.$q.screen.height
}, },
getwidth(mythis) {
// return height()
return mythis.$q.screen.width
},
isIsoDate(str) {
if (!/\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}.\d{3}Z/.test(str)) return false
const d = new Date(str)
return d.toISOString() === str
},
getLastDateReadReset() { getLastDateReadReset() {
return new Date(1999, 1, 1, 0, 0, 0) return new Date(1999, 1, 1, 0, 0, 0)
}, },
isBitActive(bit, whattofind) { isBitActive(bit, whattofind) {
return ((bit & whattofind) === whattofind) return ((bit & whattofind) === whattofind)
},
SetBit(myval, bit) {
myval = myval | bit
return myval
},
getUnique(arr, comp) {
const unique = arr
.map(e => e[comp])
// store the keys of the unique objects
.map((e, i, final) => final.indexOf(e) === i && i)
// eliminate the dead keys & store unique objects
.filter(e => arr[e]).map(e => arr[e])
return unique
} }
// getLocale() { // getLocale() {

View File

@@ -25,9 +25,23 @@
} }
.chat_dest{ .chat_dest{
display: flex;
flex: 1;
justify-content: flex-start;
} }
.chat_my{ .chat_my{
display: flex;
flex: 1;
justify-content: flex-end;
}
.bottomfixed {
// right: 0;
// position: fixed;
z-index: 9999;
// box-sizing: border-box;
margin-right: 0;
margin-left: auto;
// bottom: 0;
} }

View File

@@ -4,10 +4,15 @@ import { toolsext } from '../../store/Modules/toolsext'
import { MessageStore, UserStore } from '../../store/Modules' import { MessageStore, UserStore } from '../../store/Modules'
import globalroutines from '../../globalroutines/index' import globalroutines from '../../globalroutines/index'
import { tools } from '../../store/Modules/tools' import { tools } from '../../store/Modules/tools'
import MixinUsers from '../../mixins/mixin-users'
import { IChat, IMessage, IUserState } from '../../model' import { IChat, IMessage, IUserState, MsgDefault, StatusMessage } from '../../model'
import { Getter } from 'vuex-class' import { Getter } from 'vuex-class'
import { IMsgUsers } from '../../model/MessageStore' import { IMsgUsers } from '../../model/MessageStore'
import MixinUsers from '../../mixins/mixin-users'
import { scroll } from 'quasar'
const { getScrollTarget, setScrollPosition } = scroll
// import {Loading, QSpinnerFacebook, QSpinnerGears} from 'quasar' // import {Loading, QSpinnerFacebook, QSpinnerGears} from 'quasar'
@@ -16,7 +21,7 @@ const namespace = 'MessageModule'
@Component({ @Component({
name: 'Messages', name: 'Messages',
mixins: [MixinUsers], mixins: [MixinUsers],
components: { } components: {}
}) })
export default class Messages extends Vue { export default class Messages extends Vue {
@@ -24,18 +29,97 @@ export default class Messages extends Vue {
public $q public $q
public mydrawer = true public mydrawer = true
public miniState = false public miniState = false
public usernameloading: string = ''
public widthdrawer = 300
public chatsel: IChat = { public chatsel: IChat = {
username: '', username: '',
lasttimeActive: new Date() lasttimeActive: new Date()
} }
public mytexttosend: string = ''
public loading: boolean = false
// public users_msg_saved: IMsgUsers[] = []
@Getter('getlasts_messages', { namespace }) @Getter('getlasts_messages', { namespace })
public lasts_messages: (state: IUserState) => IMessage[] public lasts_messages: (state: IUserState) => IMessage[]
@Watch('$route.params.un')
public changeusername() {
if (this.$route.params.un === undefined || this.$route.params.un === ':un') {
this.usernameloading = this.getLastUserChatted()
} else {
this.usernameloading = this.$route.params.un
}
if (!this.miniState && tools.isMobile()) {
this.miniState = true
}
if (this.usernameloading) {
// Retrieve last msgs data from the server
this.refreshdata(this.usernameloading)
}
}
get styletextbar() {
let mystr = ''
if (this.mydrawer) {
if (!this.miniState)
mystr = `left: ${this.widthdrawer}px;`
else
mystr = `left: 57px;`
} else {
mystr = 'left: 0;'
}
// console.log('tools.getwidth', tools.getwidth)
mystr += ` width: ${tools.getwidth(this) - this.widthdrawer - 40 - 300}px; `
return mystr
}
public scrollToElement(el) {
const target = getScrollTarget(el)
const offset = el.offsetTop
const duration = 1000
// console.log('target', target, 'offset', offset, 'duration', duration)
setScrollPosition(target, offset, duration)
}
public refreshdata(username: string) {
this.loading = true
this.chatsel.username = ''
return MessageStore.actions.updateMsgDataFromServer({
username,
lastdataread: this.getlastdataread(username)
}).then((ris) => {
this.usernameloading = username
this.chatsel.username = username
this.loading = false
const element = document.getElementById('last')
this.scrollToElement(element)
// this.changemsgs('', '')
}).catch((err) => {
this.loading = false
})
}
public showNotif(msgcode) { public showNotif(msgcode) {
tools.showNotif(this.$q, this.$t(msgcode)) tools.showNotif(this.$q, this.$t(msgcode))
} }
public getMyUsername() {
return UserStore.state.my.username
}
public drawerClick(e) { public drawerClick(e) {
// if in "mini" state and user // if in "mini" state and user
// click on drawer, we switch it to "normal" mode // click on drawer, we switch it to "normal" mode
@@ -58,41 +142,106 @@ export default class Messages extends Vue {
return this.chatsel.username === username return this.chatsel.username === username
} }
@Watch('$route.params.un') public getLastUserChatted() {
public changeusername() { const lastmsg: IMessage = MessageStore.getters.getlasts_messages().slice(-1)[0]
this.chatsel.username = this.$route.params.un console.log('lastmsg', lastmsg)
if (!this.miniState && tools.isMobile()) { if (lastmsg) {
this.miniState = true return (lastmsg.origin.username !== this.getMyUsername()) ? lastmsg.origin.username : lastmsg.origin.username
} else {
return ''
} }
// Retrieve last msgs data from the server
MessageStore.actions.updateMsgDataFromServer({username: this.chatsel.username, lastdataread: this.getlastdataread() } )
} }
public selChat(mymsg: IMessage) { public selChat(mymsg: IMessage) {
this.$router.replace('/messages/' + mymsg.dest.username) if (this.chatsel.username !== mymsg.dest.username)
this.$router.replace('/messages/' + mymsg.dest.username)
else {
// refresh
this.refreshdata(this.chatsel.username)
}
} }
public msgchat(): IMsgUsers { // @Watch('MessageStore.state.users_msg', { immediate: false, deep: true })
// public changemsgs(value: string, oldValue: string) {
// console.log('changemsgs')
//
// const myrec = MessageStore.state.users_msg.find((rec) => rec.username === this.usernameloading)
//
// console.log('myrec', myrec)
//
// if (this.users_msg_saved.length < 0)
// this.users_msg_saved = []
//
// this.users_msg_saved[this.usernameloading] = {...myrec}
// console.log('this.users_msg_saved', this.users_msg_saved[this.usernameloading])
// }
public msgchat(username): IMsgUsers {
// Get msg for this chat // Get msg for this chat
return MessageStore.state.users_msg.find((rec) => rec.username === this.chatsel.username) return MessageStore.state.users_msg.find((rec) => rec.username === username)
// return this.users_msg_saved[username]
} }
public msgchat_records(): IMessage[] { public msgchat_records(): IMessage[] {
const myrec = this.msgchat() const myrec = this.msgchat(this.chatsel.username)
console.log('myrec', myrec) // console.log('msgchat_records', myrec)
// Get msg for this chat // Get msg for this chat
return (myrec) ? myrec.msgs : [] return (myrec) ? myrec.msgs : []
} }
public getlastdataread(): Date { public getlastdataread(username): any {
const myrec = this.msgchat() const myrec = this.msgchat(username)
// Get msg for this chat // Get msg for this chat
return (myrec) ? tools.gettimestampByDate(myrec.lastdataread) : tools.getLastDateReadReset() const lastdata = (myrec) ? myrec.lastdataread : tools.getLastDateReadReset()
console.table(myrec)
let mydate = ''
if (!tools.isIsoDate(lastdata))
mydate = lastdata.toISOString()
else
return lastdata
// console.log('getlastdataread', mydate)
return mydate
} }
public getMsgText(msg: IMessage) { public sendMsg() {
return [msg.message] const self = this
const data: IMessage = {
dest: {
idapp: process.env.APP_ID,
username: this.chatsel.username
},
message: this.mytexttosend
}
data.dest.username = this.chatsel.username
data.message = this.mytexttosend
this.mytexttosend = ''
MessageStore.actions.SendMsgEvent(data).then((ris) => {
data.status = StatusMessage.Sending
const element = document.getElementById('last')
this.scrollToElement(element)
if (!ris)
tools.showNegativeNotif(self.$q, self.$t('cal.sendmsg_error'))
// tools.showPositiveNotif(self.$q, self.$t('cal.sendmsg_sent'))
// else
})
}
public loadMorePosts() {
console.log('loadMorePosts')
}
public myonScroll({ target: { scrollTop, clientHeight, scrollHeight }}) {
if (scrollTop + clientHeight >= scrollHeight) {
this.loadMorePosts()
}
} }
public created() { public created() {

View File

@@ -1,5 +1,5 @@
<template> <template>
<div> <div class="q-pr-md">
<q-layout view="hHh Lpr lff" container :style="`height: ` + getheight + `px`" <q-layout view="hHh Lpr lff" container :style="`height: ` + getheight + `px`"
class="shadow-2 rounded-borders messages_page"> class="shadow-2 rounded-borders messages_page">
<q-drawer <q-drawer
@@ -8,7 +8,7 @@
:mini="!mydrawer || miniState" :mini="!mydrawer || miniState"
@click.capture="drawerClick" @click.capture="drawerClick"
:width="300" :width="widthdrawer"
:breakpoint="300" :breakpoint="300"
bordered bordered
content-class="bg-grey-3"> content-class="bg-grey-3">
@@ -36,14 +36,14 @@
<q-item-section avatar> <q-item-section avatar>
<q-avatar> <q-avatar>
<img :src="getImgByUsername(msg.dest.username)"> <img :src="getImgByMsg(msg)">
</q-avatar> </q-avatar>
</q-item-section> </q-item-section>
<q-item-section> <q-item-section>
<q-item-label lines="1">{{getUserByUsername(msg.dest.username)}}</q-item-label> <q-item-label lines="1">{{getUsernameChatByMsg(msg)}}</q-item-label>
<q-item-label caption lines="2"> <q-item-label caption lines="2">
{{msg.message}} {{getMsgText(msg, false)}}
</q-item-label> </q-item-label>
</q-item-section> </q-item-section>
@@ -68,62 +68,102 @@
</div> </div>
</q-drawer> </q-drawer>
<q-page-container> <div class="row column">
<q-page class="q-px-lg q-py-md"> <div>
<div> <q-page-container style="">
<q-item clickable v-if="!!chatsel.username"> <q-page class="q-px-lg q-py-md">
<div>
<q-item clickable v-if="!!chatsel.username" @scroll="myonScroll">
<q-item-section avatar> <q-item-section avatar>
<q-avatar> <q-avatar>
<img :src="getImgByUsername(chatsel.username)"> <img :src="getImgByUsername(chatsel.username)">
</q-avatar>
</q-item-section>
<q-item-section>
<q-item-label lines="1">{{getUserByUsername(chatsel.username)}}</q-item-label>
<q-item-label caption lines="2">
{{func_tools.getDateTimeShortStr(chatsel.lasttimeActive)}}
</q-item-label>
</q-item-section>
</q-item>
</div>
<q-separator/>
<div class="q-pa-md row" style="flex-direction: column;">
<q-item clickable v-for="(msg, index) in msgchat_records()" :key="index" v-if="msg.dest">
<div class="chat_dest" v-if="msg.dest.username === getMyUsername()">
<q-chat-message
:name="getUserByUsername(msg.origin.username)"
:text="getMsgText(msg)"
:stamp="tools.getstrDateTimeShort(msg.datemsg)"
text-color="black"
bg-color="grey-2">
<template v-slot:avatar>
<q-avatar size="sm">
<img :src="getImgByUsername(msg.origin.username)">
</q-avatar> </q-avatar>
</template> </q-item-section>
</q-chat-message>
</div>
<div class="chat_my" v-else>
<q-chat-message
name="me"
:text="getMsgText(msg)"
:stamp="tools.getstrDateTimeShort(msg.datemsg)"
sent
bg-color="blue-2">
<template v-slot:avatar>
<q-avatar size="sm">
<img :src="getMyImg">
</q-avatar>
</template>
</q-chat-message> <q-item-section>
<q-item-label lines="1">{{getUserByUsername(chatsel.username)}}</q-item-label>
<q-item-label caption lines="2">
{{func_tools.getDateTimeShortStr(chatsel.lasttimeActive)}}
</q-item-label>
</q-item-section>
</q-item>
</div> </div>
<q-separator/>
<div class="q-pa-md">
<q-item clickable v-for="(msg, index) in msgchat_records()" :key="index"
v-if="msg.dest">
</q-item> <div class="chat_dest" v-if="msg.dest.username === getMyUsername()">
<q-chat-message
:name="getUsernameChatByMsg(msg)"
:text="getMsgText(msg, true)"
:stamp="tools.getstrDateTimeShort(msg.datemsg)"
text-color="black"
bg-color="grey-2">
<template v-slot:avatar>
<q-avatar size="sm">
<img :src="getImgByMsg(msg)">
</q-avatar>
</template>
</q-chat-message>
</div>
<div class="chat_my" v-else>
<q-chat-message
name="me"
:text="getMsgText(msg, true)"
:stamp="tools.getstrDateTimeShort(msg.datemsg)"
sent
bg-color="blue-2">
<template v-slot:avatar>
<q-avatar size="sm">
<img :src="getMyImg">
</q-avatar>
</template>
</q-chat-message>
</div>
</q-item>
<div id="last"></div>
<q-inner-loading id="spinner" :showing="loading">
<q-spinner-tail
color="primary"
size="4em">
</q-spinner-tail>
</q-inner-loading>
</div>
</q-page>
</q-page-container>
</div>
<div class="bottomfixed row" :style="styletextbar">
<div class="" style="max-width: 50px; align-self: center; order: 1;">
<q-btn rounded
size="sm"
icon="fas fa-smile">
</q-btn>
</div> </div>
</q-page> <div class="" style="max-height: 100px; flex-grow:1; order: 2;">
</q-page-container> <q-input
bordered
rounded
v-model="mytexttosend"
debounce="1000"
filled
autogrow
input-style="max-height: 95px;">
</q-input>
</div>
<div class="" style="max-width: 50px; align-self: center; order: 3;">
<q-btn push
rounded
size="sm"
icon="send"
@click="sendMsg">
</q-btn>
</div>
</div>
</div>
</q-layout> </q-layout>
</div> </div>
</template> </template>