Style quick reactions.
Issue #65. Move "edit" and "delete" to emoji picker dialog, accessible through the "..." button.
This commit is contained in:
parent
779e53c3b2
commit
61dbcad131
6 changed files with 193 additions and 71 deletions
|
|
@ -377,15 +377,24 @@ $admin-fg: white;
|
||||||
.message-operations {
|
.message-operations {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
width: fit-content;
|
width: fit-content;
|
||||||
background-color: #e2e2e2;
|
background-color: white;
|
||||||
|
height: 40px;
|
||||||
|
border-radius: 20px;
|
||||||
|
box-shadow: 4px 4px 8px #888888;
|
||||||
// &.incoming {
|
// &.incoming {
|
||||||
// right: 30%;
|
// right: 30%;
|
||||||
// }
|
// }
|
||||||
// &.outgoing {
|
// &.outgoing {
|
||||||
// left: 30%;
|
// left: unset !important;
|
||||||
|
// right: 10px !important;
|
||||||
// }
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.message-operations-picker {
|
||||||
|
background-color: white;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
.quick-reaction-container {
|
.quick-reaction-container {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
background-color: #000000;
|
background-color: #000000;
|
||||||
|
|
|
||||||
|
|
@ -13,16 +13,19 @@
|
||||||
>
|
>
|
||||||
<div ref="messageOperationsStrut" class="message-operations-strut">
|
<div ref="messageOperationsStrut" class="message-operations-strut">
|
||||||
<message-operations
|
<message-operations
|
||||||
|
ref="messageOperations"
|
||||||
:style="opStyle"
|
:style="opStyle"
|
||||||
|
:emojis="recentEmojis"
|
||||||
v-on:close="showContextMenu = false"
|
v-on:close="showContextMenu = false"
|
||||||
v-if="selectedEvent && showContextMenu"
|
v-if="selectedEvent && showContextMenu"
|
||||||
v-on:addreaction="addReaction"
|
v-on:addreaction="addReaction"
|
||||||
|
v-on:addquickreaction="addQuickReaction"
|
||||||
v-on:addreply="addReply(selectedEvent)"
|
v-on:addreply="addReply(selectedEvent)"
|
||||||
v-on:edit="edit(selectedEvent)"
|
v-on:edit="edit(selectedEvent)"
|
||||||
v-on:redact="redact(selectedEvent)"
|
v-on:redact="redact(selectedEvent)"
|
||||||
v-on:download="download(selectedEvent)"
|
v-on:download="download(selectedEvent)"
|
||||||
|
v-on:more="showMoreMessageOperations"
|
||||||
:event="selectedEvent"
|
:event="selectedEvent"
|
||||||
:incoming="selectedEvent.getSender() != $matrix.currentUserId"
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
@ -255,11 +258,22 @@
|
||||||
</v-dialog>
|
</v-dialog>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-if="showEmojiPicker">
|
<v-dialog v-model="showEmojiPicker" class="ma-0 pa-0" eager>
|
||||||
<v-dialog v-model="showEmojiPicker" class="ma-0 pa-0">
|
<div>
|
||||||
<VEmojiPicker style="width: 100%" @select="emojiSelected" />
|
<MessageOperationsPicker
|
||||||
</v-dialog>
|
v-on:close="showEmojiPicker = false"
|
||||||
</div>
|
v-if="selectedEvent"
|
||||||
|
v-on:addreaction="addReaction"
|
||||||
|
v-on:addquickreaction="addQuickReaction"
|
||||||
|
v-on:addreply="addReply(selectedEvent)"
|
||||||
|
v-on:edit="edit(selectedEvent)"
|
||||||
|
v-on:redact="redact(selectedEvent)"
|
||||||
|
v-on:download="download(selectedEvent)"
|
||||||
|
:event="selectedEvent"
|
||||||
|
/>
|
||||||
|
<VEmojiPicker ref="emojiPicker" style="width: 100%" @select="emojiSelected" />
|
||||||
|
</div>
|
||||||
|
</v-dialog>
|
||||||
|
|
||||||
<!-- "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%">
|
||||||
|
|
@ -321,6 +335,7 @@ import RoomJoinRules from "./messages/RoomJoinRules.vue";
|
||||||
import DebugEvent from "./messages/DebugEvent.vue";
|
import DebugEvent from "./messages/DebugEvent.vue";
|
||||||
import util from "../plugins/utils";
|
import util from "../plugins/utils";
|
||||||
import MessageOperations from "./messages/MessageOperations.vue";
|
import MessageOperations from "./messages/MessageOperations.vue";
|
||||||
|
import MessageOperationsPicker from "./messages/MessageOperationsPicker.vue";
|
||||||
import ChatHeader from "./ChatHeader";
|
import ChatHeader from "./ChatHeader";
|
||||||
import VoiceRecorder from "./VoiceRecorder";
|
import VoiceRecorder from "./VoiceRecorder";
|
||||||
import RoomInfoBottomSheet from "./RoomInfoBottomSheet";
|
import RoomInfoBottomSheet from "./RoomInfoBottomSheet";
|
||||||
|
|
@ -379,6 +394,7 @@ export default {
|
||||||
RoomJoinRules,
|
RoomJoinRules,
|
||||||
DebugEvent,
|
DebugEvent,
|
||||||
MessageOperations,
|
MessageOperations,
|
||||||
|
MessageOperationsPicker,
|
||||||
VoiceRecorder,
|
VoiceRecorder,
|
||||||
RoomInfoBottomSheet,
|
RoomInfoBottomSheet,
|
||||||
CreatedRoomWelcomeHeader
|
CreatedRoomWelcomeHeader
|
||||||
|
|
@ -430,7 +446,10 @@ export default {
|
||||||
lastRR: null,
|
lastRR: null,
|
||||||
|
|
||||||
/** If we just created this room, show a small welcome header with info */
|
/** If we just created this room, show a small welcome header with info */
|
||||||
showCreatedRoomWelcomeHeader: false
|
showCreatedRoomWelcomeHeader: false,
|
||||||
|
|
||||||
|
/** An array of recent emojis. Used in the "message operations" popup. */
|
||||||
|
recentEmojis: []
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
@ -522,6 +541,9 @@ export default {
|
||||||
var rectChat = this.$refs.messageOperationsStrut.getBoundingClientRect();
|
var rectChat = this.$refs.messageOperationsStrut.getBoundingClientRect();
|
||||||
top = rectAnchor.top - rectChat.top;
|
top = rectAnchor.top - rectChat.top;
|
||||||
left = rectAnchor.left - rectChat.left;
|
left = rectAnchor.left - rectChat.left;
|
||||||
|
if (left + 250 > rectChat.right) {
|
||||||
|
left = rectChat.right - 250; // Pretty ugly, but we want to make sure it does not escape the screen, and we don't have the exakt width of it (yet)!
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return "top:" + top + "px;left:" + left + "px";
|
return "top:" + top + "px;left:" + left + "px";
|
||||||
|
|
@ -713,6 +735,7 @@ export default {
|
||||||
* Triggered when our "long tap" timer hits.
|
* Triggered when our "long tap" timer hits.
|
||||||
*/
|
*/
|
||||||
touchTimerElapsed() {
|
touchTimerElapsed() {
|
||||||
|
this.updateRecentEmojis();
|
||||||
this.showContextMenu = true;
|
this.showContextMenu = true;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
@ -1040,6 +1063,10 @@ export default {
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
showMoreMessageOperations(e) {
|
||||||
|
this.addReaction(e);
|
||||||
|
},
|
||||||
|
|
||||||
addReaction(e) {
|
addReaction(e) {
|
||||||
const event = e.event;
|
const event = e.event;
|
||||||
// Store the event we are reacting to, so that we know where to
|
// Store the event we are reacting to, so that we know where to
|
||||||
|
|
@ -1048,6 +1075,10 @@ export default {
|
||||||
this.showEmojiPicker = true;
|
this.showEmojiPicker = true;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
addQuickReaction(e) {
|
||||||
|
this.sendQuickReaction({ reaction: e.emoji, event: e.event });
|
||||||
|
},
|
||||||
|
|
||||||
addReply(event) {
|
addReply(event) {
|
||||||
this.replyToEvent = event;
|
this.replyToEvent = event;
|
||||||
this.$refs.messageInput.focus();
|
this.$refs.messageInput.focus();
|
||||||
|
|
@ -1124,6 +1155,7 @@ export default {
|
||||||
console.log("Got the ref", ref);
|
console.log("Got the ref", ref);
|
||||||
}
|
}
|
||||||
this.selectedEvent = event;
|
this.selectedEvent = event;
|
||||||
|
this.updateRecentEmojis();
|
||||||
this.showContextMenu = true;
|
this.showContextMenu = true;
|
||||||
this.showContextMenuAnchor = e.anchor;
|
this.showContextMenuAnchor = e.anchor;
|
||||||
},
|
},
|
||||||
|
|
@ -1258,7 +1290,16 @@ export default {
|
||||||
// can see all messages).
|
// can see all messages).
|
||||||
this.onScroll();
|
this.onScroll();
|
||||||
});
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
updateRecentEmojis() {
|
||||||
|
if (this.$refs.emojiPicker) {
|
||||||
|
this.recentEmojis = this.$refs.emojiPicker.mapEmojis["Frequently"];
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.recentEmojis = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
|
||||||
|
|
@ -1,86 +1,54 @@
|
||||||
<template>
|
<template>
|
||||||
<div :class="{'message-operations':true,'incoming':incoming,'outgoing':!incoming}">
|
<div :class="{'message-operations':true,'incoming':incoming,'outgoing':!incoming}">
|
||||||
<v-btn icon @click.stop="addReaction" class="ma-0 pa-0">
|
<template v-for="(item,index) in emojis">
|
||||||
<v-icon>mood</v-icon>
|
<v-btn v-if="index < maxRecents" :key="item.data" icon @click.stop="addQuickReaction(item.data)" class="ma-0 pa-0">
|
||||||
</v-btn>
|
<span class="recent-emoji" >{{ item.data }}</span>
|
||||||
|
</v-btn>
|
||||||
|
</template>
|
||||||
<v-btn v-if="incoming" icon @click.stop="addReply" class="ma-0 pa-0">
|
<v-btn v-if="incoming" icon @click.stop="addReply" class="ma-0 pa-0">
|
||||||
<v-icon>reply</v-icon>
|
<v-icon>reply</v-icon>
|
||||||
</v-btn>
|
</v-btn>
|
||||||
<v-btn v-if="isEditable" icon @click.stop="edit" class="ma-0 pa-0">
|
<v-btn icon @click.stop="more" class="ma-0 pa-0">
|
||||||
<v-icon>edit</v-icon>
|
<v-icon>more_horiz</v-icon>
|
||||||
</v-btn>
|
</v-btn> </div>
|
||||||
<v-btn v-if="isRedactable" icon @click.stop="redact" class="ma-0 pa-0">
|
|
||||||
<v-icon>delete</v-icon>
|
|
||||||
</v-btn>
|
|
||||||
<v-btn v-if="isDownloadable" icon @click.stop="download" class="ma-0 pa-0">
|
|
||||||
<v-icon>get_app</v-icon>
|
|
||||||
</v-btn>
|
|
||||||
</div>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import messageMixin from "./messageMixin";
|
import messageMixin from "./messageMixin";
|
||||||
|
import messageOperationsMixin from "./messageOperationsMixin";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
mixins: [messageMixin],
|
mixins: [messageMixin, messageOperationsMixin],
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
maxRecents: 5
|
||||||
|
}
|
||||||
|
},
|
||||||
props: {
|
props: {
|
||||||
incoming: {
|
emojis: {
|
||||||
type: Boolean,
|
type: Array,
|
||||||
default: function () {
|
default: function () {
|
||||||
return true
|
return []
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
event: {
|
|
||||||
type: Object,
|
|
||||||
default: function () {
|
|
||||||
return {}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
watch: {
|
||||||
isEditable() {
|
emojis: {
|
||||||
return !this.incoming && this.event.getContent().msgtype == "m.text";
|
immediate: true,
|
||||||
},
|
handler(newVal, oldVal) {
|
||||||
isDownloadable() {
|
console.log("Emojis changed", newVal, oldVal);
|
||||||
const msgtype = this.event.getContent().msgtype;
|
|
||||||
return ['m.video','m.audio','m.image','m.file'].includes(msgtype);
|
|
||||||
},
|
|
||||||
isRedactable() {
|
|
||||||
const room = this.$matrix.matrixClient.getRoom(this.event.getRoomId());
|
|
||||||
if (room && room.currentState && room.currentState.maySendRedactionForEvent(this.event, this.$matrix.currentUserId)) {
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
return false;
|
},
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
|
||||||
addReaction() {
|
|
||||||
this.$emit("close");
|
|
||||||
this.$emit("addreaction", {event:this.event});
|
|
||||||
},
|
|
||||||
addReply() {
|
|
||||||
this.$emit("close");
|
|
||||||
this.$emit("addreply", {event:this.event});
|
|
||||||
},
|
|
||||||
edit() {
|
|
||||||
this.$emit("close");
|
|
||||||
this.$emit("edit", {event:this.event});
|
|
||||||
},
|
|
||||||
redact() {
|
|
||||||
this.$emit("close");
|
|
||||||
this.$emit("redact", {event:this.event});
|
|
||||||
},
|
|
||||||
download() {
|
|
||||||
this.$emit("close");
|
|
||||||
this.$emit("download", {event:this.event});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
@import "@/assets/css/chat.scss";
|
@import "@/assets/css/chat.scss";
|
||||||
|
|
||||||
|
// .recent-emoji {
|
||||||
|
// width: 30px;
|
||||||
|
// }
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
51
src/components/messages/MessageOperationsPicker.vue
Normal file
51
src/components/messages/MessageOperationsPicker.vue
Normal file
|
|
@ -0,0 +1,51 @@
|
||||||
|
<template>
|
||||||
|
<div
|
||||||
|
:class="{
|
||||||
|
'message-operations-picker': true,
|
||||||
|
incoming: incoming,
|
||||||
|
outgoing: !incoming,
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<v-container fluid>
|
||||||
|
<v-row>
|
||||||
|
<v-col v-if="incoming">
|
||||||
|
<v-btn icon @click.stop="addReply" class="ma-0 pa-0">
|
||||||
|
<v-icon>reply</v-icon>
|
||||||
|
</v-btn>
|
||||||
|
<div>Reply</div>
|
||||||
|
</v-col>
|
||||||
|
<v-col v-if="isEditable">
|
||||||
|
<v-btn icon @click.stop="edit" class="ma-0 pa-0">
|
||||||
|
<v-icon>edit</v-icon>
|
||||||
|
</v-btn>
|
||||||
|
<div>Edit</div>
|
||||||
|
</v-col>
|
||||||
|
<v-col v-if="isRedactable">
|
||||||
|
<v-btn icon @click.stop="redact" class="ma-0 pa-0">
|
||||||
|
<v-icon>delete</v-icon>
|
||||||
|
</v-btn>
|
||||||
|
<div>Delete</div>
|
||||||
|
</v-col>
|
||||||
|
<v-col v-if="isDownloadable">
|
||||||
|
<v-btn icon @click.stop="download" class="ma-0 pa-0">
|
||||||
|
<v-icon>get_app</v-icon>
|
||||||
|
</v-btn>
|
||||||
|
<div>Download</div>
|
||||||
|
</v-col>
|
||||||
|
</v-row>
|
||||||
|
</v-container>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import messageMixin from "./messageMixin";
|
||||||
|
import messageOperationsMixin from "./messageOperationsMixin";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
mixins: [messageMixin, messageOperationsMixin],
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
@import "@/assets/css/chat.scss";
|
||||||
|
</style>
|
||||||
|
|
@ -62,6 +62,10 @@ export default {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
incoming() {
|
||||||
|
return this.event && this.event.getSender() != this.$matrix.currentUserId;
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Don't show sender and time if the next event is within 2 minutes and also from us (= back to back messages)
|
* Don't show sender and time if the next event is within 2 minutes and also from us (= back to back messages)
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
49
src/components/messages/messageOperationsMixin.js
Normal file
49
src/components/messages/messageOperationsMixin.js
Normal file
|
|
@ -0,0 +1,49 @@
|
||||||
|
|
||||||
|
export default {
|
||||||
|
computed: {
|
||||||
|
isEditable() {
|
||||||
|
return !this.incoming && this.event.getContent().msgtype == "m.text";
|
||||||
|
},
|
||||||
|
isDownloadable() {
|
||||||
|
const msgtype = this.event.getContent().msgtype;
|
||||||
|
return ['m.video','m.audio','m.image','m.file'].includes(msgtype);
|
||||||
|
},
|
||||||
|
isRedactable() {
|
||||||
|
const room = this.$matrix.matrixClient.getRoom(this.event.getRoomId());
|
||||||
|
if (room && room.currentState && room.currentState.maySendRedactionForEvent(this.event, this.$matrix.currentUserId)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
addReaction() {
|
||||||
|
this.$emit("close");
|
||||||
|
this.$emit("addreaction", {event:this.event});
|
||||||
|
},
|
||||||
|
addQuickReaction(emoji) {
|
||||||
|
this.$emit("close");
|
||||||
|
this.$emit("addquickreaction", {event:this.event,emoji:emoji});
|
||||||
|
},
|
||||||
|
addReply() {
|
||||||
|
this.$emit("close");
|
||||||
|
this.$emit("addreply", {event:this.event});
|
||||||
|
},
|
||||||
|
edit() {
|
||||||
|
this.$emit("close");
|
||||||
|
this.$emit("edit", {event:this.event});
|
||||||
|
},
|
||||||
|
redact() {
|
||||||
|
this.$emit("close");
|
||||||
|
this.$emit("redact", {event:this.event});
|
||||||
|
},
|
||||||
|
download() {
|
||||||
|
this.$emit("close");
|
||||||
|
this.$emit("download", {event:this.event});
|
||||||
|
},
|
||||||
|
more() {
|
||||||
|
this.$emit("close");
|
||||||
|
this.$emit("more", {event:this.event});
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue