Break message layouts out into separate compontents
This commit is contained in:
parent
d221d94b6c
commit
9c8efab236
11 changed files with 349 additions and 96 deletions
|
|
@ -8,97 +8,53 @@
|
||||||
>
|
>
|
||||||
<div v-for="event in events" :key="event.eventId">
|
<div v-for="event in events" :key="event.eventId">
|
||||||
<!-- Contact joined the chat -->
|
<!-- Contact joined the chat -->
|
||||||
<div
|
<ContactJoin
|
||||||
class="messageJoin"
|
class="messageJoin"
|
||||||
v-if="
|
v-if="
|
||||||
event.event.state_key != myUserId &&
|
event.event.state_key != myUserId &&
|
||||||
event.getContent().membership == 'join' &&
|
event.getContent().membership == 'join' &&
|
||||||
event.getType() == 'm.room.member'
|
event.getType() == 'm.room.member'
|
||||||
"
|
" :room="room" :event="event"
|
||||||
>
|
/>
|
||||||
{{ stateEventDisplayName(event) }} joined the chat
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Contact left the chat -->
|
<!-- Contact left the chat -->
|
||||||
<div
|
<ContactLeave
|
||||||
class="messageJoin"
|
class="messageJoin"
|
||||||
v-if="
|
v-if="
|
||||||
event.event.state_key != myUserId &&
|
event.event.state_key != myUserId &&
|
||||||
event.getContent().membership == 'leave' &&
|
event.getContent().membership == 'leave' &&
|
||||||
event.getType() == 'm.room.member'
|
event.getType() == 'm.room.member'
|
||||||
"
|
" :room="room" :event="event"
|
||||||
>
|
/>
|
||||||
{{ stateEventDisplayName(event) }} left the chat
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Contact invited to the chat -->
|
<!-- Contact invited to the chat -->
|
||||||
<div
|
<ContactInvited
|
||||||
class="messageJoin"
|
class="messageJoin"
|
||||||
v-if="
|
v-if="
|
||||||
event.event.state_key != myUserId &&
|
event.event.state_key != myUserId &&
|
||||||
event.getContent().membership == 'invite' &&
|
event.getContent().membership == 'invite' &&
|
||||||
event.getType() == 'm.room.member'
|
event.getType() == 'm.room.member'
|
||||||
"
|
" :room="room" :event="event"
|
||||||
>
|
/>
|
||||||
{{ stateEventDisplayName(event) }} was invited to the chat...
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div
|
<MessageIncomingText
|
||||||
v-else-if="
|
v-else-if="
|
||||||
event.getSender() != myUserId && event.getType() == 'm.room.message'
|
event.getSender() != myUserId && event.getType() == 'm.room.message'
|
||||||
"
|
" :room="room" :event="event"
|
||||||
>
|
/>
|
||||||
<div class="messageIn">
|
|
||||||
<div class="sender">{{ messageEventDisplayName(event) }}</div>
|
|
||||||
<v-avatar class="avatar" size="40" color="grey">
|
|
||||||
<img
|
|
||||||
v-if="messageEventAvatar(event)"
|
|
||||||
:src="messageEventAvatar(event)"
|
|
||||||
/>
|
|
||||||
<span v-else class="white--text headline">{{
|
|
||||||
messageEventDisplayName(event).substring(0, 1).toUpperCase()
|
|
||||||
}}</span>
|
|
||||||
</v-avatar>
|
|
||||||
|
|
||||||
<div class="bubble">
|
<MessageOutgoingText v-else-if="event.getType() == 'm.room.message'" :room="room" :event="event" />
|
||||||
<div class="message">{{ event.getContent().body }}</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="time">
|
|
||||||
{{ formatTime(event.event.origin_server_ts) }}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div v-else-if="event.getType() == 'm.room.message'">
|
|
||||||
<div class="messageOut">
|
|
||||||
<div class="sender">{{ "You" }}</div>
|
|
||||||
<div class="bubble">
|
|
||||||
<div class="message">{{ event.getContent().body }}</div>
|
|
||||||
</div>
|
|
||||||
<div class="status">{{ event.status }}</div>
|
|
||||||
</div>
|
|
||||||
<div class="time">
|
|
||||||
{{ formatTime(event.event.origin_server_ts) }}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- ROOM NAME CHANGED -->
|
<!-- ROOM NAME CHANGED -->
|
||||||
<div v-else-if="event.getType() == 'm.room.name'" class="statusEvent">
|
<RoomNameChanged v-else-if="event.getType() == 'm.room.name'" :room="room" :event="event" />
|
||||||
{{ stateEventDisplayName(event) }} changed room name to
|
|
||||||
{{ event.getContent().name }}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- ROOM TOPIC CHANGED -->
|
<!-- ROOM TOPIC CHANGED -->
|
||||||
<div v-else-if="event.getType() == 'm.room.topic'" class="statusEvent">
|
<RoomTopicChanged v-else-if="event.getType() == 'm.room.topic'" :room="room" :event="event" />
|
||||||
{{ stateEventDisplayName(event) }} changed topic to
|
|
||||||
{{ event.getContent().topic }}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- ROOM AVATAR CHANGED -->
|
<!-- ROOM AVATAR CHANGED -->
|
||||||
<div v-else-if="event.getType() == 'm.room.avatar'" class="statusEvent">
|
<RoomAvatarChanged v-else-if="event.getType() == 'm.room.avatar'" :room="room" :event="event" />
|
||||||
{{ stateEventDisplayName(event) }} changed the room avatar
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div v-else class="statusEvent">Event: {{ event.getType() }}</div>
|
<DebugEvent v-else :room="room" :event="event" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
@ -169,6 +125,15 @@
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { TimelineWindow, EventTimeline } from "matrix-js-sdk";
|
import { TimelineWindow, EventTimeline } from "matrix-js-sdk";
|
||||||
|
import MessageIncomingText from './messages/MessageIncomingText';
|
||||||
|
import MessageOutgoingText from './messages/MessageOutgoingText';
|
||||||
|
import ContactJoin from './messages/ContactJoin.vue';
|
||||||
|
import ContactLeave from './messages/ContactLeave.vue';
|
||||||
|
import ContactInvited from './messages/ContactInvited.vue';
|
||||||
|
import RoomNameChanged from './messages/RoomNameChanged.vue';
|
||||||
|
import RoomTopicChanged from './messages/RoomTopicChanged.vue';
|
||||||
|
import RoomAvatarChanged from './messages/RoomAvatarChanged.vue';
|
||||||
|
import DebugEvent from "./messages/DebugEvent.vue";
|
||||||
|
|
||||||
// from https://kirbysayshi.com/2013/08/19/maintaining-scroll-position-knockoutjs-list.html
|
// from https://kirbysayshi.com/2013/08/19/maintaining-scroll-position-knockoutjs-list.html
|
||||||
function ScrollPosition(node) {
|
function ScrollPosition(node) {
|
||||||
|
|
@ -200,7 +165,19 @@ ScrollPosition.prototype.prepareFor = function (direction) {
|
||||||
export default {
|
export default {
|
||||||
name: "Chat",
|
name: "Chat",
|
||||||
|
|
||||||
data: () => ({
|
components: {
|
||||||
|
MessageIncomingText,
|
||||||
|
MessageOutgoingText,
|
||||||
|
ContactJoin,
|
||||||
|
ContactLeave,
|
||||||
|
ContactInvited,
|
||||||
|
RoomNameChanged,
|
||||||
|
RoomTopicChanged,
|
||||||
|
RoomAvatarChanged,
|
||||||
|
DebugEvent
|
||||||
|
},
|
||||||
|
|
||||||
|
data() { return {
|
||||||
room: null,
|
room: null,
|
||||||
events: [],
|
events: [],
|
||||||
currentInput: "",
|
currentInput: "",
|
||||||
|
|
@ -212,7 +189,7 @@ export default {
|
||||||
currentSendOperation: null,
|
currentSendOperation: null,
|
||||||
currentSendProgress: null,
|
currentSendProgress: null,
|
||||||
currentSendError: null,
|
currentSendError: null,
|
||||||
}),
|
}},
|
||||||
|
|
||||||
mounted() {
|
mounted() {
|
||||||
const container = this.$refs.chatContainer;
|
const container = this.$refs.chatContainer;
|
||||||
|
|
@ -313,39 +290,6 @@ export default {
|
||||||
console.log("Typing:", event);
|
console.log("Typing:", event);
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
|
||||||
* Get a display name given an event.
|
|
||||||
*/
|
|
||||||
stateEventDisplayName(event) {
|
|
||||||
if (this.room) {
|
|
||||||
const member = this.room.getMember(event.getSender());
|
|
||||||
if (member) {
|
|
||||||
return member.name;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return event.getContent().displayname || event.event.state_key;
|
|
||||||
},
|
|
||||||
|
|
||||||
messageEventDisplayName(event) {
|
|
||||||
return this.stateEventDisplayName(event);
|
|
||||||
},
|
|
||||||
|
|
||||||
messageEventAvatar(event) {
|
|
||||||
if (this.room) {
|
|
||||||
const member = this.room.getMember(event.getSender());
|
|
||||||
if (member) {
|
|
||||||
return member.getAvatarUrl(
|
|
||||||
this.$matrix.matrixClient.getHomeserverUrl(),
|
|
||||||
40,
|
|
||||||
40,
|
|
||||||
"scale",
|
|
||||||
true
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
},
|
|
||||||
|
|
||||||
sendMessage() {
|
sendMessage() {
|
||||||
if (this.currentInput.length > 0) {
|
if (this.currentInput.length > 0) {
|
||||||
this.sendMatrixMessage(this.currentInput);
|
this.sendMatrixMessage(this.currentInput);
|
||||||
|
|
@ -540,6 +484,40 @@ export default {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a display name given an event.
|
||||||
|
*/
|
||||||
|
stateEventDisplayName(event) {
|
||||||
|
if (this.room) {
|
||||||
|
const member = this.room.getMember(event.getSender());
|
||||||
|
if (member) {
|
||||||
|
return member.name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return event.getContent().displayname || event.event.state_key;
|
||||||
|
},
|
||||||
|
|
||||||
|
messageEventDisplayName(event) {
|
||||||
|
return this.stateEventDisplayName(event);
|
||||||
|
},
|
||||||
|
|
||||||
|
messageEventAvatar(event) {
|
||||||
|
if (this.room) {
|
||||||
|
const member = this.room.getMember(event.getSender());
|
||||||
|
if (member) {
|
||||||
|
return member.getAvatarUrl(
|
||||||
|
this.$matrix.matrixClient.getHomeserverUrl(),
|
||||||
|
40,
|
||||||
|
40,
|
||||||
|
"scale",
|
||||||
|
true
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
|
||||||
25
src/components/messages/ContactInvited.vue
Normal file
25
src/components/messages/ContactInvited.vue
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
<template>
|
||||||
|
<!-- Contact invited to the chat -->
|
||||||
|
<div
|
||||||
|
class="messageJoin"
|
||||||
|
v-if="
|
||||||
|
event.event.state_key != myUserId &&
|
||||||
|
event.getContent().membership == 'invite' &&
|
||||||
|
event.getType() == 'm.room.member'
|
||||||
|
"
|
||||||
|
>
|
||||||
|
{{ stateEventDisplayName(event) }} was invited to the chat...
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import messageMixin from "./messageMixin";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
mixins: [messageMixin],
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
@import "@/assets/css/chat.scss";
|
||||||
|
</style>
|
||||||
25
src/components/messages/ContactJoin.vue
Normal file
25
src/components/messages/ContactJoin.vue
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
<template>
|
||||||
|
<!-- Contact joined the chat -->
|
||||||
|
<div
|
||||||
|
class="messageJoin"
|
||||||
|
v-if="
|
||||||
|
event.event.state_key != myUserId &&
|
||||||
|
event.getContent().membership == 'join' &&
|
||||||
|
event.getType() == 'm.room.member'
|
||||||
|
"
|
||||||
|
>
|
||||||
|
{{ stateEventDisplayName(event) }} joined the chat
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import messageMixin from "./messageMixin";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
mixins: [messageMixin],
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
@import "@/assets/css/chat.scss";
|
||||||
|
</style>
|
||||||
25
src/components/messages/ContactLeave.vue
Normal file
25
src/components/messages/ContactLeave.vue
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
<template>
|
||||||
|
<!-- Contact left the chat -->
|
||||||
|
<div
|
||||||
|
class="messageJoin"
|
||||||
|
v-if="
|
||||||
|
event.event.state_key != myUserId &&
|
||||||
|
event.getContent().membership == 'leave' &&
|
||||||
|
event.getType() == 'm.room.member'
|
||||||
|
"
|
||||||
|
>
|
||||||
|
{{ stateEventDisplayName(event) }} left the chat
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import messageMixin from "./messageMixin";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
mixins: [messageMixin],
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
@import "@/assets/css/chat.scss";
|
||||||
|
</style>
|
||||||
15
src/components/messages/DebugEvent.vue
Normal file
15
src/components/messages/DebugEvent.vue
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
<template>
|
||||||
|
<div class="statusEvent">Event: {{ event.getType() }}</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import messageMixin from "./messageMixin";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
mixins: [messageMixin],
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
@import "@/assets/css/chat.scss";
|
||||||
|
</style>
|
||||||
35
src/components/messages/MessageIncomingText.vue
Normal file
35
src/components/messages/MessageIncomingText.vue
Normal file
|
|
@ -0,0 +1,35 @@
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<div class="messageIn">
|
||||||
|
<div class="sender">{{ messageEventDisplayName(event) }}</div>
|
||||||
|
<v-avatar class="avatar" size="40" color="grey">
|
||||||
|
<img
|
||||||
|
v-if="messageEventAvatar(event)"
|
||||||
|
:src="messageEventAvatar(event)"
|
||||||
|
/>
|
||||||
|
<span v-else class="white--text headline">{{
|
||||||
|
messageEventDisplayName(event).substring(0, 1).toUpperCase()
|
||||||
|
}}</span>
|
||||||
|
</v-avatar>
|
||||||
|
|
||||||
|
<div class="bubble">
|
||||||
|
<div class="message">{{ event.getContent().body }}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="time">
|
||||||
|
{{ formatTime(event.event.origin_server_ts) }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import messageMixin from "./messageMixin";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
mixins: [messageMixin],
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
@import "@/assets/css/chat.scss";
|
||||||
|
</style>
|
||||||
26
src/components/messages/MessageOutgoingText.vue
Normal file
26
src/components/messages/MessageOutgoingText.vue
Normal file
|
|
@ -0,0 +1,26 @@
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<div class="messageOut">
|
||||||
|
<div class="sender">{{ "You" }}</div>
|
||||||
|
<div class="bubble">
|
||||||
|
<div class="message">{{ event.getContent().body }}</div>
|
||||||
|
</div>
|
||||||
|
<div class="status">{{ event.status }}</div>
|
||||||
|
</div>
|
||||||
|
<div class="time">
|
||||||
|
{{ formatTime(event.event.origin_server_ts) }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import messageMixin from "./messageMixin";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
mixins: [messageMixin],
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
@import "@/assets/css/chat.scss";
|
||||||
|
</style>
|
||||||
18
src/components/messages/RoomAvatarChanged.vue
Normal file
18
src/components/messages/RoomAvatarChanged.vue
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
<template>
|
||||||
|
<!-- ROOM AVATAR CHANGED -->
|
||||||
|
<div class="statusEvent">
|
||||||
|
{{ stateEventDisplayName(event) }} changed the room avatar
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import messageMixin from "./messageMixin";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
mixins: [messageMixin],
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
@import "@/assets/css/chat.scss";
|
||||||
|
</style>
|
||||||
19
src/components/messages/RoomNameChanged.vue
Normal file
19
src/components/messages/RoomNameChanged.vue
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
<template>
|
||||||
|
<!-- ROOM NAME CHANGED -->
|
||||||
|
<div class="statusEvent">
|
||||||
|
{{ stateEventDisplayName(event) }} changed room name to
|
||||||
|
{{ event.getContent().name }}
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import messageMixin from "./messageMixin";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
mixins: [messageMixin],
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
@import "@/assets/css/chat.scss";
|
||||||
|
</style>
|
||||||
19
src/components/messages/RoomTopicChanged.vue
Normal file
19
src/components/messages/RoomTopicChanged.vue
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
<template>
|
||||||
|
<!-- ROOM TOPIC CHANGED -->
|
||||||
|
<div class="statusEvent">
|
||||||
|
{{ stateEventDisplayName(event) }} changed topic to
|
||||||
|
{{ event.getContent().topic }}
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import messageMixin from "./messageMixin";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
mixins: [messageMixin],
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
@import "@/assets/css/chat.scss";
|
||||||
|
</style>
|
||||||
68
src/components/messages/messageMixin.js
Normal file
68
src/components/messages/messageMixin.js
Normal file
|
|
@ -0,0 +1,68 @@
|
||||||
|
export default {
|
||||||
|
props: {
|
||||||
|
room: {
|
||||||
|
type: Object,
|
||||||
|
default: function () {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
event: {
|
||||||
|
type: Object,
|
||||||
|
default: function () {
|
||||||
|
return {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
/**
|
||||||
|
* Get a display name given an event.
|
||||||
|
*/
|
||||||
|
stateEventDisplayName(event) {
|
||||||
|
if (this.room) {
|
||||||
|
const member = this.room.getMember(event.getSender());
|
||||||
|
if (member) {
|
||||||
|
return member.name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return event.getContent().displayname || event.event.state_key;
|
||||||
|
},
|
||||||
|
|
||||||
|
messageEventDisplayName(event) {
|
||||||
|
return this.stateEventDisplayName(event);
|
||||||
|
},
|
||||||
|
|
||||||
|
messageEventAvatar(event) {
|
||||||
|
if (this.room) {
|
||||||
|
const member = this.room.getMember(event.getSender());
|
||||||
|
if (member) {
|
||||||
|
return member.getAvatarUrl(
|
||||||
|
this.$matrix.matrixClient.getHomeserverUrl(),
|
||||||
|
40,
|
||||||
|
40,
|
||||||
|
"scale",
|
||||||
|
true
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
|
||||||
|
formatTime(time) {
|
||||||
|
const date = new Date();
|
||||||
|
date.setTime(time);
|
||||||
|
|
||||||
|
const today = new Date();
|
||||||
|
if (
|
||||||
|
date.getDate() == today.getDate() &&
|
||||||
|
date.getMonth() == today.getMonth() &&
|
||||||
|
date.getFullYear() == today.getFullYear()
|
||||||
|
) {
|
||||||
|
// For today, skip the date part
|
||||||
|
return date.toLocaleTimeString();
|
||||||
|
}
|
||||||
|
return date.toLocaleString();
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue