Context menu on messages
This commit is contained in:
parent
5635b12ca4
commit
6530de4bf2
1 changed files with 74 additions and 5 deletions
|
@ -1,21 +1,31 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="flex flex-col grow shrink thread overflow-y-auto mb-4 -mr-4 pr-4">
|
<div class="flex flex-col grow shrink thread overflow-y-auto mb-4 -mr-4 pr-4">
|
||||||
<template v-for="(event, i) in threadItem.timeline">
|
<form @submit.prevent="finishEditing" v-if="isEditing">
|
||||||
|
<input id="message_input" class="w-full" autofocus type="text" v-model="editedMessage" />
|
||||||
|
</form>
|
||||||
|
<template v-else v-for="(event, i) in threadItem.timeline">
|
||||||
<template v-if="event.type === 'message'">
|
<template v-if="event.type === 'message'">
|
||||||
<div v-if="i === threadItem.timeline.length - 1 || threadItem.timeline[i + 1].type !== 'message' || threadItem.timeline[i + 1].contact !== event.contact" style="max-width: 80%;" class="flex mb-2" :class="event.contact === sourceObj.selfContact ? 'self self-end flex-row-reverse' : 'other self-start'">
|
<div v-if="i === threadItem.timeline.length - 1 || threadItem.timeline[i + 1].type !== 'message' || threadItem.timeline[i + 1].contact !== event.contact" style="max-width: 80%;" class="flex mb-2" :class="event.contact === sourceObj.selfContact ? 'self self-end flex-row-reverse' : 'other self-start'" @mousedown="start(i)" @mouseup="stop" @contextmenu="e => openmenu(e, i)">
|
||||||
<Avatar v-if="event.contact !== sourceObj.selfContact" v-bind="sourceObj.contacts[event.contact]" class="self-end" />
|
<Avatar v-if="event.contact !== sourceObj.selfContact" v-bind="sourceObj.contacts[event.contact]" class="self-end" />
|
||||||
<div class="flex flex-col">
|
<div class="flex flex-col">
|
||||||
<span class="message px-2 py-1 flex items-center rounded-2xl" :class="getRoundedCornersClasses(i) + (event.contact === sourceObj.selfContact ? ' ml-13' : ' ml-2')">{{ event.message }}</span>
|
<span class="message px-2 py-1 flex items-center rounded-2xl" :class="getRoundedCornersClasses(i) + (event.contact === sourceObj.selfContact ? ' ml-13' : ' ml-2')">{{ event.message }}</span>
|
||||||
<span v-if="event.contact !== sourceObj.selfContact" class="author px-2 text-sm italic mx-2">{{ sourceObj.contacts[event.contact].name }}</span>
|
<span v-if="event.contact !== sourceObj.selfContact" class="author px-2 text-sm italic mx-2">{{ sourceObj.contacts[event.contact].name }}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div v-else style="max-width: 80%;" class="flex mb-px" :class="event.contact === sourceObj.selfContact ? 'self self-end flex-row-reverse' : 'other self-start text-gray-200'">
|
<div v-else class="flex mb-px" :class="event.contact === sourceObj.selfContact ? 'self self-end flex-row-reverse' : 'other self-start text-gray-200'" @mousedown="start(i)" @mouseup="stop" @contextmenu="e => openmenu(e, i)">
|
||||||
<span class="message px-2 py-1 flex items-center rounded-2xl ml-13" :class="getRoundedCornersClasses(i)">{{ event.message }}</span>
|
<span class="message px-2 py-1 flex items-center rounded-2xl ml-13" :class="getRoundedCornersClasses(i)">{{ event.message }}</span>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<div v-else-if="event.type === 'create-thread'" class="self-center mb-2 italic text-gray-500 text-center">
|
<div v-else-if="event.type === 'create-thread'" class="self-center mb-2 italic text-gray-500 text-center" @mousedown="start(i)" @mouseup="stop" @contextmenu="e => openmenu(e, i)">
|
||||||
{{ sourceObj.contacts[event.contact].name }} created a new thread called "<RouterLink :to="`${baseUrl}/${source}/${sourceItem}/${event.thread}`" class="text-blue-500">{{ item.threads[event.thread].title }}</RouterLink>"
|
{{ sourceObj.contacts[event.contact].name }} created a new thread called "<RouterLink :to="`${baseUrl}/${source}/${sourceItem}/${event.thread}`" class="text-blue-500">{{ item.threads[event.thread].title }}</RouterLink>"
|
||||||
</div>
|
</div>
|
||||||
|
<div v-if="selectedMessage === i" class="bg-gray-300 flex justify-around mb-4 p-2 text-blue-500">
|
||||||
|
<button v-if="ownsMessage && event.type === 'message'" @click="startEditing">Edit</button>
|
||||||
|
<button v-if="ownsMessage" @click="deleteMessage(i)">Delete</button>
|
||||||
|
<button>Create thread</button>
|
||||||
|
<button>Create to do</button>
|
||||||
|
<button>Add to garden</button>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
<form @submit="sendMessage" class="shrink-0">
|
<form @submit="sendMessage" class="shrink-0">
|
||||||
|
@ -24,7 +34,7 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed, ref } from 'vue';
|
import { computed, onUnmounted, ref, watch } from 'vue';
|
||||||
import { items, sources } from '../state';
|
import { items, sources } from '../state';
|
||||||
import Avatar from './Avatar.vue';
|
import Avatar from './Avatar.vue';
|
||||||
import { RouterLink } from 'vue-router';
|
import { RouterLink } from 'vue-router';
|
||||||
|
@ -91,6 +101,65 @@ function getRoundedCornersClasses(index: number) {
|
||||||
}
|
}
|
||||||
return classes;
|
return classes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const timeout = ref<number>();
|
||||||
|
const selectedMessageIndex = ref<number>();
|
||||||
|
|
||||||
|
function start(index: number) {
|
||||||
|
if (!timeout.value) {
|
||||||
|
timeout.value = setTimeout(handleHolding, 500);
|
||||||
|
selectedMessageIndex.value = index;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function stop() {
|
||||||
|
if (timeout.value) {
|
||||||
|
clearTimeout(timeout.value);
|
||||||
|
selectedMessageIndex.value = undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function handleHolding() {
|
||||||
|
timeout.value = undefined;
|
||||||
|
}
|
||||||
|
function openmenu(e: MouseEvent | TouchEvent, index: number) {
|
||||||
|
timeout.value = undefined;
|
||||||
|
selectedMessageIndex.value = index;
|
||||||
|
e.preventDefault();
|
||||||
|
}
|
||||||
|
|
||||||
|
const selectedMessage = computed(() => timeout.value != null || selectedMessageIndex == null ? undefined : selectedMessageIndex.value);
|
||||||
|
const ownsMessage = computed(() => selectedMessage.value != null && threadItem.value.timeline[selectedMessage.value].type === "message" && threadItem.value.timeline[selectedMessage.value].contact === sourceObj.value.selfContact);
|
||||||
|
|
||||||
|
onUnmounted(stop);
|
||||||
|
document.onclick = () => (selectedMessageIndex.value = undefined);
|
||||||
|
|
||||||
|
const editedMessage = ref("");
|
||||||
|
const isEditing = ref(false);
|
||||||
|
function startEditing() {
|
||||||
|
isEditing.value = true;
|
||||||
|
editedMessage.value = "";
|
||||||
|
}
|
||||||
|
function finishEditing() {
|
||||||
|
if (selectedMessage.value == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const event = threadItem.value.timeline[selectedMessage.value];
|
||||||
|
if (event.type !== "message") {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
event.message = editedMessage.value;
|
||||||
|
selectedMessageIndex.value = undefined;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
watch(selectedMessage, index => {
|
||||||
|
if (index == null) {
|
||||||
|
isEditing.value = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
function deleteMessage(event: number) {
|
||||||
|
threadItem.value.timeline.splice(event, 1);
|
||||||
|
selectedMessageIndex.value = undefined;
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
|
Loading…
Reference in a new issue