Context menu on messages

This commit is contained in:
thepaperpilot 2024-04-27 01:38:47 -05:00
parent 5635b12ca4
commit 6530de4bf2

View file

@ -1,21 +1,31 @@
<template>
<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'">
<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" />
<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 v-if="event.contact !== sourceObj.selfContact" class="author px-2 text-sm italic mx-2">{{ sourceObj.contacts[event.contact].name }}</span>
</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>
</div>
</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>"
</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>
</div>
<form @submit="sendMessage" class="shrink-0">
@ -24,7 +34,7 @@
</template>
<script setup lang="ts">
import { computed, ref } from 'vue';
import { computed, onUnmounted, ref, watch } from 'vue';
import { items, sources } from '../state';
import Avatar from './Avatar.vue';
import { RouterLink } from 'vue-router';
@ -91,6 +101,65 @@ function getRoundedCornersClasses(index: number) {
}
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>
<style scoped>