parent
0aa33c5300
commit
da30d6899a
9 changed files with 490 additions and 3 deletions
|
|
@ -39,3 +39,12 @@ npm run lint
|
||||||
|
|
||||||
### Customize configuration
|
### Customize configuration
|
||||||
See [Configuration Reference](https://cli.vuejs.org/config/).
|
See [Configuration Reference](https://cli.vuejs.org/config/).
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
## Theming
|
||||||
|
|
||||||
|
# Sticker short codes - To enable sticker short codes, follow there steps:
|
||||||
|
* set "useShortCodeStickers" to true in config.json.
|
||||||
|
* Add your sticker pack folders (containing stickers) to /src/assets/stickers/
|
||||||
|
* Create file /src/assets/stickers/order.txt that lists the folders names in order, one folder name per line.
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
{
|
{
|
||||||
"defaultServer": "https://neo.keanu.im"
|
"defaultServer": "https://neo.keanu.im",
|
||||||
|
"useShortCodeStickers": false
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -197,6 +197,15 @@ $admin-fg: white;
|
||||||
width: 70%;
|
width: 70%;
|
||||||
max-width: 70%;
|
max-width: 70%;
|
||||||
}
|
}
|
||||||
|
.bubble.sticker-bubble {
|
||||||
|
padding: 0px;
|
||||||
|
overflow: hidden;
|
||||||
|
display: inline-block;
|
||||||
|
width: 30%;
|
||||||
|
max-width: 30%;
|
||||||
|
background-color: transparent;
|
||||||
|
border: 0px solid transparent !important;
|
||||||
|
}
|
||||||
.avatar {
|
.avatar {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
vertical-align: top !important;
|
vertical-align: top !important;
|
||||||
|
|
@ -261,6 +270,13 @@ $admin-fg: white;
|
||||||
border-radius: 10px 10px 0 10px;
|
border-radius: 10px 10px 0 10px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.bubble.sticker-bubble {
|
||||||
|
padding: 0px;
|
||||||
|
display: inline-block;
|
||||||
|
width: 30%;
|
||||||
|
max-width: 30%;
|
||||||
|
background-color: transparent;
|
||||||
|
}
|
||||||
.avatar {
|
.avatar {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
vertical-align: bottom !important;
|
vertical-align: bottom !important;
|
||||||
|
|
|
||||||
184
src/components/BottomSheet.vue
Normal file
184
src/components/BottomSheet.vue
Normal file
|
|
@ -0,0 +1,184 @@
|
||||||
|
<template>
|
||||||
|
<div
|
||||||
|
class="bottom-sheet"
|
||||||
|
ref="sheet"
|
||||||
|
:style="state == 'closed' ? { 'pointer-events': 'none' } : {}"
|
||||||
|
>
|
||||||
|
<v-fade-transition>
|
||||||
|
<div
|
||||||
|
class="bottom-sheet-bg"
|
||||||
|
v-show="state != 'closed'"
|
||||||
|
@click.stop="onBackgroundClick"
|
||||||
|
/>
|
||||||
|
</v-fade-transition>
|
||||||
|
<div
|
||||||
|
class="bottom-sheet-content"
|
||||||
|
:data-state="isMove ? 'move' : state"
|
||||||
|
ref="pan"
|
||||||
|
:style="{ top: `${isMove ? y : calcY()}px` }"
|
||||||
|
>
|
||||||
|
<div class="bottom-sheet-handle"><div class="bottom-sheet-handle-decoration" /></div>
|
||||||
|
<div style="position:absolute;top:20px;left:0;right:0;bottom:0;overflow-y:auto">
|
||||||
|
<slot></slot>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import Hammer from "hammerjs";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
props: {
|
||||||
|
openY: {
|
||||||
|
type: Number,
|
||||||
|
default: 0.1,
|
||||||
|
},
|
||||||
|
halfY: {
|
||||||
|
type: Number,
|
||||||
|
default: 0.5,
|
||||||
|
},
|
||||||
|
defaultState: {
|
||||||
|
type: String,
|
||||||
|
default: "closed",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
mc: null,
|
||||||
|
y: 0,
|
||||||
|
startY: 0,
|
||||||
|
isMove: false,
|
||||||
|
state: this.defaultState,
|
||||||
|
rect: {},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
window.onresize = () => {
|
||||||
|
this.rect = this.$refs.sheet.getBoundingClientRect();
|
||||||
|
};
|
||||||
|
this.rect = this.$refs.sheet.getBoundingClientRect();
|
||||||
|
|
||||||
|
this.mc = new Hammer(this.$refs.pan);
|
||||||
|
this.mc.get("pan").set({ direction: Hammer.DIRECTION_ALL });
|
||||||
|
|
||||||
|
const self = this;
|
||||||
|
|
||||||
|
this.mc.on("panup pandown", (evt) => {
|
||||||
|
self.y = evt.center.y - 16;
|
||||||
|
});
|
||||||
|
|
||||||
|
this.mc.on("panstart", (evt) => {
|
||||||
|
self.startY = evt.center.y;
|
||||||
|
self.isMove = true;
|
||||||
|
});
|
||||||
|
|
||||||
|
this.mc.on("panend", (evt) => {
|
||||||
|
self.isMove = false;
|
||||||
|
|
||||||
|
switch (self.state) {
|
||||||
|
case "small":
|
||||||
|
if (self.startY - evt.center.y > 120) {
|
||||||
|
self.state = "open";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (self.startY - evt.center.y < -50) {
|
||||||
|
self.state = "closed";
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "open":
|
||||||
|
if (self.startY - evt.center.y < -120) {
|
||||||
|
self.state = "small";
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
beforeDestroy() {
|
||||||
|
this.mc.destroy();
|
||||||
|
window.onresize = null;
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
calcY() {
|
||||||
|
switch (this.state) {
|
||||||
|
case "closed":
|
||||||
|
return this.rect.height;
|
||||||
|
case "open":
|
||||||
|
return this.rect.height * this.openY;
|
||||||
|
case "small":
|
||||||
|
return this.rect.height * this.halfY;
|
||||||
|
default:
|
||||||
|
return this.y;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
open() {
|
||||||
|
this.setState("small");
|
||||||
|
},
|
||||||
|
close() {
|
||||||
|
this.setState("closed");
|
||||||
|
},
|
||||||
|
setState(state) {
|
||||||
|
this.state = state;
|
||||||
|
},
|
||||||
|
onBackgroundClick() {
|
||||||
|
if (this.state == "open") {
|
||||||
|
this.setState("small");
|
||||||
|
} else {
|
||||||
|
this.setState("closed");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.bottom-sheet {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
overflow-x: hidden;
|
||||||
|
overflow-y: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bottom-sheet-bg {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
background-color: rgba(black, 0.4);
|
||||||
|
}
|
||||||
|
|
||||||
|
.bottom-sheet-handle {
|
||||||
|
height: 20px;
|
||||||
|
background-color: white;
|
||||||
|
position: relative;
|
||||||
|
.bottom-sheet-handle-decoration {
|
||||||
|
background-color: #cccccc;
|
||||||
|
height: 2px;
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
left: 30%;
|
||||||
|
right: 30%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.bottom-sheet-content {
|
||||||
|
position: absolute;
|
||||||
|
top: 0px;
|
||||||
|
bottom: 0px;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
border-radius: 10px 10px 0px 0px;
|
||||||
|
background-color: white;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bottom-sheet-content[data-state="small"],
|
||||||
|
.bottom-sheet-content[data-state="open"],
|
||||||
|
.bottom-sheet-content[data-state="closed"] {
|
||||||
|
transition: top 0.3s ease-out;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
@ -195,6 +195,19 @@
|
||||||
</v-btn>
|
</v-btn>
|
||||||
</v-col>
|
</v-col>
|
||||||
|
|
||||||
|
<v-col v-if="config.useShortCodeStickers" class="input-area-button text-center flex-grow-0 flex-shrink-1">
|
||||||
|
<v-btn
|
||||||
|
v-if="!showRecorder"
|
||||||
|
icon
|
||||||
|
large
|
||||||
|
color="black"
|
||||||
|
@click="showStickerPicker"
|
||||||
|
:disabled="attachButtonDisabled"
|
||||||
|
>
|
||||||
|
<v-icon large>face</v-icon>
|
||||||
|
</v-btn>
|
||||||
|
</v-col>
|
||||||
|
|
||||||
<v-col class="input-area-button text-center flex-grow-0 flex-shrink-1">
|
<v-col class="input-area-button text-center flex-grow-0 flex-shrink-1">
|
||||||
<label icon flat ref="attachmentLabel">
|
<label icon flat ref="attachmentLabel">
|
||||||
<v-btn
|
<v-btn
|
||||||
|
|
@ -275,6 +288,8 @@
|
||||||
<VEmojiPicker ref="emojiPicker" style="width: 100%" @select="emojiSelected" />
|
<VEmojiPicker ref="emojiPicker" style="width: 100%" @select="emojiSelected" />
|
||||||
</MessageOperationsBottomSheet>
|
</MessageOperationsBottomSheet>
|
||||||
|
|
||||||
|
<StickerPickerBottomSheet ref="stickerPickerSheet" style="z-index:10" v-on:selectSticker="sendSticker" />
|
||||||
|
|
||||||
<!-- "NOT ALLOWED FOR GUEST ACCOUNTS" dialog -->
|
<!-- "NOT ALLOWED FOR GUEST ACCOUNTS" dialog -->
|
||||||
<v-dialog v-model="showNotAllowedForGuests" class="ma-0 pa-0" width="50%">
|
<v-dialog v-model="showNotAllowedForGuests" class="ma-0 pa-0" width="50%">
|
||||||
<v-card>
|
<v-card>
|
||||||
|
|
@ -320,11 +335,13 @@ import MessageIncomingFile from "./messages/MessageIncomingFile";
|
||||||
import MessageIncomingImage from "./messages/MessageIncomingImage.vue";
|
import MessageIncomingImage from "./messages/MessageIncomingImage.vue";
|
||||||
import MessageIncomingAudio from "./messages/MessageIncomingAudio.vue";
|
import MessageIncomingAudio from "./messages/MessageIncomingAudio.vue";
|
||||||
import MessageIncomingVideo from "./messages/MessageIncomingVideo.vue";
|
import MessageIncomingVideo from "./messages/MessageIncomingVideo.vue";
|
||||||
|
import MessageIncomingSticker from "./messages/MessageIncomingSticker.vue";
|
||||||
import MessageOutgoingText from "./messages/MessageOutgoingText";
|
import MessageOutgoingText from "./messages/MessageOutgoingText";
|
||||||
import MessageOutgoingFile from "./messages/MessageOutgoingFile";
|
import MessageOutgoingFile from "./messages/MessageOutgoingFile";
|
||||||
import MessageOutgoingImage from "./messages/MessageOutgoingImage.vue";
|
import MessageOutgoingImage from "./messages/MessageOutgoingImage.vue";
|
||||||
import MessageOutgoingAudio from "./messages/MessageOutgoingAudio.vue";
|
import MessageOutgoingAudio from "./messages/MessageOutgoingAudio.vue";
|
||||||
import MessageOutgoingVideo from "./messages/MessageOutgoingVideo.vue";
|
import MessageOutgoingVideo from "./messages/MessageOutgoingVideo.vue";
|
||||||
|
import MessageOutgoingSticker from "./messages/MessageOutgoingSticker.vue";
|
||||||
import ContactJoin from "./messages/ContactJoin.vue";
|
import ContactJoin from "./messages/ContactJoin.vue";
|
||||||
import ContactLeave from "./messages/ContactLeave.vue";
|
import ContactLeave from "./messages/ContactLeave.vue";
|
||||||
import ContactInvited from "./messages/ContactInvited.vue";
|
import ContactInvited from "./messages/ContactInvited.vue";
|
||||||
|
|
@ -343,6 +360,10 @@ import VoiceRecorder from "./VoiceRecorder";
|
||||||
import RoomInfoBottomSheet from "./RoomInfoBottomSheet";
|
import RoomInfoBottomSheet from "./RoomInfoBottomSheet";
|
||||||
import CreatedRoomWelcomeHeader from "./CreatedRoomWelcomeHeader";
|
import CreatedRoomWelcomeHeader from "./CreatedRoomWelcomeHeader";
|
||||||
import MessageOperationsBottomSheet from './MessageOperationsBottomSheet';
|
import MessageOperationsBottomSheet from './MessageOperationsBottomSheet';
|
||||||
|
import stickers from '../plugins/stickers';
|
||||||
|
import StickerPickerBottomSheet from './StickerPickerBottomSheet';
|
||||||
|
import BottomSheet from './BottomSheet.vue';
|
||||||
|
import config from "../assets/config";
|
||||||
|
|
||||||
const READ_RECEIPT_TIMEOUT = 5000; /* How long a message must have been visible before the read marker is updated */
|
const READ_RECEIPT_TIMEOUT = 5000; /* How long a message must have been visible before the read marker is updated */
|
||||||
const WINDOW_BUFFER_SIZE = 0.3 /** Relative window height of when we start paginating. Always keep this much loaded before and after our scroll position! */
|
const WINDOW_BUFFER_SIZE = 0.3 /** Relative window height of when we start paginating. Always keep this much loaded before and after our scroll position! */
|
||||||
|
|
@ -384,11 +405,13 @@ export default {
|
||||||
MessageIncomingImage,
|
MessageIncomingImage,
|
||||||
MessageIncomingAudio,
|
MessageIncomingAudio,
|
||||||
MessageIncomingVideo,
|
MessageIncomingVideo,
|
||||||
|
MessageIncomingSticker,
|
||||||
MessageOutgoingText,
|
MessageOutgoingText,
|
||||||
MessageOutgoingFile,
|
MessageOutgoingFile,
|
||||||
MessageOutgoingImage,
|
MessageOutgoingImage,
|
||||||
MessageOutgoingAudio,
|
MessageOutgoingAudio,
|
||||||
MessageOutgoingVideo,
|
MessageOutgoingVideo,
|
||||||
|
MessageOutgoingSticker,
|
||||||
ContactJoin,
|
ContactJoin,
|
||||||
ContactLeave,
|
ContactLeave,
|
||||||
ContactInvited,
|
ContactInvited,
|
||||||
|
|
@ -404,11 +427,14 @@ export default {
|
||||||
VoiceRecorder,
|
VoiceRecorder,
|
||||||
RoomInfoBottomSheet,
|
RoomInfoBottomSheet,
|
||||||
CreatedRoomWelcomeHeader,
|
CreatedRoomWelcomeHeader,
|
||||||
MessageOperationsBottomSheet
|
MessageOperationsBottomSheet,
|
||||||
|
StickerPickerBottomSheet,
|
||||||
|
BottomSheet,
|
||||||
},
|
},
|
||||||
|
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
|
config: config,
|
||||||
events: [],
|
events: [],
|
||||||
currentInput: "",
|
currentInput: "",
|
||||||
typingMembers: [],
|
typingMembers: [],
|
||||||
|
|
@ -820,6 +846,8 @@ export default {
|
||||||
return MessageIncomingVideo;
|
return MessageIncomingVideo;
|
||||||
} else if (event.getContent().msgtype == "m.file") {
|
} else if (event.getContent().msgtype == "m.file") {
|
||||||
return MessageIncomingFile;
|
return MessageIncomingFile;
|
||||||
|
} else if (stickers.isStickerShortcode(event.getContent().body)) {
|
||||||
|
return MessageIncomingSticker;
|
||||||
}
|
}
|
||||||
return MessageIncomingText;
|
return MessageIncomingText;
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -831,6 +859,8 @@ export default {
|
||||||
return MessageOutgoingVideo;
|
return MessageOutgoingVideo;
|
||||||
} else if (event.getContent().msgtype == "m.file") {
|
} else if (event.getContent().msgtype == "m.file") {
|
||||||
return MessageOutgoingFile;
|
return MessageOutgoingFile;
|
||||||
|
} else if (stickers.isStickerShortcode(event.getContent().body)) {
|
||||||
|
return MessageOutgoingSticker;
|
||||||
}
|
}
|
||||||
return MessageOutgoingText;
|
return MessageOutgoingText;
|
||||||
}
|
}
|
||||||
|
|
@ -980,6 +1010,10 @@ export default {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
showStickerPicker() {
|
||||||
|
this.$refs.stickerPickerSheet.open();
|
||||||
|
},
|
||||||
|
|
||||||
onUploadProgress(p) {
|
onUploadProgress(p) {
|
||||||
if (p.total) {
|
if (p.total) {
|
||||||
this.currentSendProgress =
|
this.currentSendProgress =
|
||||||
|
|
@ -1200,6 +1234,10 @@ export default {
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
sendSticker(stickerShortCode) {
|
||||||
|
this.sendMessage(stickerShortCode);
|
||||||
|
},
|
||||||
|
|
||||||
showContextMenuForEvent(e) {
|
showContextMenuForEvent(e) {
|
||||||
const event = e.event;
|
const event = e.event;
|
||||||
const ref = this.$refs[event.getId()];
|
const ref = this.$refs[event.getId()];
|
||||||
|
|
|
||||||
77
src/components/StickerPickerBottomSheet.vue
Normal file
77
src/components/StickerPickerBottomSheet.vue
Normal file
|
|
@ -0,0 +1,77 @@
|
||||||
|
<template>
|
||||||
|
<BottomSheet ref="sheet" class="sticker-picker">
|
||||||
|
<v-tabs
|
||||||
|
v-model="currentStickerPack"
|
||||||
|
centered
|
||||||
|
class="tabs"
|
||||||
|
show-arrows
|
||||||
|
>
|
||||||
|
<v-tabs-slider></v-tabs-slider>
|
||||||
|
|
||||||
|
<v-tab v-for="pack in packs" :key="pack">
|
||||||
|
{{ pack }}
|
||||||
|
<v-icon>mdi-phone</v-icon>
|
||||||
|
</v-tab>
|
||||||
|
</v-tabs>
|
||||||
|
|
||||||
|
<v-tabs-items v-model="currentStickerPack" class="tab-items">
|
||||||
|
<v-tab-item v-for="pack in packs" :key="pack">
|
||||||
|
<v-card flat>
|
||||||
|
<v-container fluid>
|
||||||
|
<v-row>
|
||||||
|
<v-col cols="2" v-for="sticker in stickersInPack(pack)" :key="pack + sticker.name">
|
||||||
|
<v-img @click="selectSticker(pack, sticker)" :src="sticker.image" contain />
|
||||||
|
</v-col>
|
||||||
|
</v-row>
|
||||||
|
</v-container>
|
||||||
|
</v-card>
|
||||||
|
</v-tab-item>
|
||||||
|
</v-tabs-items>
|
||||||
|
</BottomSheet>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import BottomSheet from "./BottomSheet";
|
||||||
|
import stickers from '../plugins/stickers';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: {
|
||||||
|
BottomSheet,
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
currentStickerPack: 'tab-0',
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
packs() {
|
||||||
|
return stickers.getPacks();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {},
|
||||||
|
methods: {
|
||||||
|
open() {
|
||||||
|
this.$refs.sheet.open();
|
||||||
|
},
|
||||||
|
stickersInPack(pack) {
|
||||||
|
return stickers.stickersInPack(pack);
|
||||||
|
},
|
||||||
|
selectSticker(pack, sticker) {
|
||||||
|
this.$refs.sheet.close();
|
||||||
|
this.$emit('selectSticker', stickers.getStickerShortcode(pack, sticker));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
@import "@/assets/css/chat.scss";
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
.sticker-picker .tabs {
|
||||||
|
position: sticky;
|
||||||
|
top: 0px;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
49
src/components/messages/MessageIncomingSticker.vue
Normal file
49
src/components/messages/MessageIncomingSticker.vue
Normal file
|
|
@ -0,0 +1,49 @@
|
||||||
|
<template>
|
||||||
|
<div :class="messageClasses">
|
||||||
|
<v-avatar class="avatar" size="32" color="#ededed">
|
||||||
|
<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 sticker-bubble">
|
||||||
|
<v-img :aspect-ratio="16 / 9" ref="image" :src="src" :cover="cover" :contain="contain" />
|
||||||
|
<QuickReactions :event="event" :reactions="reactions" />
|
||||||
|
</div>
|
||||||
|
<div class="op-button" ref="opbutton">
|
||||||
|
<v-btn icon @click.stop="showContextMenu($refs.opbutton)"
|
||||||
|
><v-icon>more_vert</v-icon></v-btn
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
<div v-if="showSenderAndTime" class="senderAndTime">
|
||||||
|
<div class="sender">{{ messageEventDisplayName(event) }}</div>
|
||||||
|
<div class="time">
|
||||||
|
{{ formatTime(event.event.origin_server_ts) }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import messageMixin from "./messageMixin";
|
||||||
|
import stickers from "../../plugins/stickers";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
mixins: [messageMixin],
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
src: null,
|
||||||
|
cover: false,
|
||||||
|
contain: true,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
//console.log("Mounted with event:", JSON.stringify(this.event.getContent()));
|
||||||
|
this.src = stickers.getStickerImage(this.event.getContent().body)
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
@import "@/assets/css/chat.scss";
|
||||||
|
</style>
|
||||||
47
src/components/messages/MessageOutgoingSticker.vue
Normal file
47
src/components/messages/MessageOutgoingSticker.vue
Normal file
|
|
@ -0,0 +1,47 @@
|
||||||
|
<template>
|
||||||
|
<div class="messageOut">
|
||||||
|
<div class="op-button" ref="opbutton">
|
||||||
|
<v-btn icon @click.stop="showContextMenu($refs.opbutton)"
|
||||||
|
><v-icon>more_vert</v-icon></v-btn
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
<div class="bubble sticker-bubble">
|
||||||
|
<v-img :aspect-ratio="16 / 9" ref="image" :src="src" :cover="cover" :contain="contain" />
|
||||||
|
<QuickReactions :event="event" :reactions="reactions" />
|
||||||
|
</div>
|
||||||
|
<v-avatar class="avatar" size="32" color="#ededed" @click.stop="ownAvatarClicked">
|
||||||
|
<img v-if="userAvatar" :src="userAvatar" />
|
||||||
|
<span v-else class="white--text headline">{{ userAvatarLetter }}</span>
|
||||||
|
</v-avatar>
|
||||||
|
<div class="senderAndTime">
|
||||||
|
<!-- <div class="sender">{{ "You" }}</div> -->
|
||||||
|
<div class="time">
|
||||||
|
{{ formatTime(event.event.origin_server_ts) }}
|
||||||
|
<div class="status">{{ event.status }}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import messageMixin from "./messageMixin";
|
||||||
|
import stickers from "../../plugins/stickers";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
mixins: [messageMixin],
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
src: null,
|
||||||
|
cover: false,
|
||||||
|
contain: true,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.src = stickers.getStickerImage(this.event.getContent().body)
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
@import "@/assets/css/chat.scss";
|
||||||
|
</style>
|
||||||
66
src/plugins/stickers.js
Normal file
66
src/plugins/stickers.js
Normal file
|
|
@ -0,0 +1,66 @@
|
||||||
|
const stickerPacks = {};
|
||||||
|
stickerPacks.ordering = [];
|
||||||
|
const stickerCodeMap = {};
|
||||||
|
|
||||||
|
try {
|
||||||
|
const stickerPackInfo = require("!!raw-loader!@/assets/stickers/order.txt").default;
|
||||||
|
const packInfo = stickerPackInfo.split("\n");
|
||||||
|
for (let i = 0; i < packInfo.length; i++) {
|
||||||
|
const pack = packInfo[i];
|
||||||
|
if (pack && pack.length > 0) {
|
||||||
|
stickerPacks[pack] = [];
|
||||||
|
stickerPacks.ordering.push(pack);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (ignorederr) {
|
||||||
|
//
|
||||||
|
}
|
||||||
|
|
||||||
|
function importAll(r) {
|
||||||
|
return r.keys().map(res => {
|
||||||
|
// Remove"./"
|
||||||
|
const parts = res.split("/");
|
||||||
|
const pack = parts[1];
|
||||||
|
const sticker = parts[2].split(".")[0];
|
||||||
|
const image = r(res);
|
||||||
|
if (stickerPacks[pack] !== undefined) {
|
||||||
|
stickerPacks[pack].push({ image: image, name: sticker });
|
||||||
|
stickerCodeMap[":" + pack + "-" + sticker + ":"] = image;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
importAll(require.context('@/assets/stickers/', true, /\.png$/));
|
||||||
|
|
||||||
|
class Stickers {
|
||||||
|
constructor() {
|
||||||
|
}
|
||||||
|
|
||||||
|
isStickerShortcode(messageBody) {
|
||||||
|
if (messageBody && messageBody.startsWith(":") && messageBody.startsWith(":") && messageBody.length >= 5) {
|
||||||
|
const image = this.getStickerImage(messageBody);
|
||||||
|
return image != undefined && image != null;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
getStickerShortcode(pack, sticker) {
|
||||||
|
return ":" + pack + "-" + sticker.name + ":";
|
||||||
|
}
|
||||||
|
|
||||||
|
getPacks() {
|
||||||
|
return stickerPacks.ordering;
|
||||||
|
}
|
||||||
|
|
||||||
|
getStickerImage(messageBody) {
|
||||||
|
if (!messageBody) return null;
|
||||||
|
if (messageBody.length < 5 || !messageBody.startsWith(":") || !messageBody.endsWith(":")) return null;
|
||||||
|
return stickerCodeMap[messageBody];
|
||||||
|
}
|
||||||
|
|
||||||
|
stickersInPack(pack) {
|
||||||
|
return stickerPacks[pack];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var gStickers = new Stickers();
|
||||||
|
export default gStickers;
|
||||||
Loading…
Add table
Add a link
Reference in a new issue