Source pages allow linking to messages
This commit is contained in:
parent
d74e285447
commit
9165b6ea4a
4 changed files with 40 additions and 10 deletions
|
@ -8,17 +8,18 @@
|
||||||
<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)">
|
<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="getRoundedCornersClasses(threadItem, sourceObj.selfContact, i) +
|
<span
|
||||||
' message px-2 py-1 flex items-center rounded-2xl' +
|
:class="getClasses(i) + (event.contact === sourceObj.selfContact ? ' ml-13' : ' ml-2')"
|
||||||
(event.contact === sourceObj.selfContact ? ' ml-13' : ' ml-2')">
|
:ref="i === highlighted ? 'highlightedEvent' : undefined">
|
||||||
<component :is="parseMessage(threadItem, i)" />
|
<component :is="parseMessage(threadItem, i)" />
|
||||||
</span>
|
</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 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)">
|
<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="getRoundedCornersClasses(threadItem, sourceObj.selfContact, i) +
|
<span
|
||||||
' message px-2 py-1 flex items-center rounded-2xl ml-13'">
|
:class="getClasses(i) + ' ml-13'"
|
||||||
|
:ref="i === highlighted ? 'highlightedEvent' : undefined">
|
||||||
<component :is="parseMessage(threadItem, i)" />
|
<component :is="parseMessage(threadItem, i)" />
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
@ -54,6 +55,7 @@ const props = defineProps<{
|
||||||
sourceItem: number;
|
sourceItem: number;
|
||||||
thread: number;
|
thread: number;
|
||||||
baseUrl: string;
|
baseUrl: string;
|
||||||
|
highlighted?: number;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const sourceObj = computed(() => sources.value[props.source]);
|
const sourceObj = computed(() => sources.value[props.source]);
|
||||||
|
@ -62,12 +64,28 @@ const threadItem = computed(() => item.value.threads[props.thread]);
|
||||||
|
|
||||||
const message = ref("");
|
const message = ref("");
|
||||||
const messageInput = ref();
|
const messageInput = ref();
|
||||||
|
const highlightedEvent = ref();
|
||||||
|
|
||||||
function sendMessage() {
|
function getClasses(index: number) {
|
||||||
|
let classes = 'message px-2 py-1 flex items-center rounded-2xl';
|
||||||
|
classes += ' ' + getRoundedCornersClasses(threadItem.value, sourceObj.value.selfContact, index);
|
||||||
|
if (props.highlighted === index) {
|
||||||
|
classes += ' animate-ping highlight';
|
||||||
|
}
|
||||||
|
return classes;
|
||||||
|
}
|
||||||
|
|
||||||
|
watch(highlightedEvent, element => {
|
||||||
|
element[0]?.scrollIntoView();
|
||||||
|
})
|
||||||
|
|
||||||
|
function sendMessage(e: Event) {
|
||||||
if (message.value.trim() === "") {
|
if (message.value.trim() === "") {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
threadItem.value.timeline.push({
|
threadItem.value.timeline.push({
|
||||||
type: "message",
|
type: "message",
|
||||||
contact: sourceObj.value.selfContact,
|
contact: sourceObj.value.selfContact,
|
||||||
|
@ -245,4 +263,9 @@ function createToDo(event: number) {
|
||||||
.mr-13 {
|
.mr-13 {
|
||||||
margin-right: 3.25rem;
|
margin-right: 3.25rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.highlight {
|
||||||
|
animation-delay: .5s;
|
||||||
|
animation-iteration-count: 1;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -12,11 +12,13 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="hidden xl:flex bg-slate-200 ml-0 h-screen flex-col overflow-hidden" :class="showThread ? 'p-4 basis-6/12' : 'basis-0'">
|
<div class="hidden xl:flex bg-slate-200 ml-0 h-screen flex-col overflow-hidden" :class="showThread ? 'p-4 basis-6/12' : 'basis-0'">
|
||||||
<PanelTitle :title="threadTitle" :isRead="isRead" @toggleRead="toggleRead" />
|
<PanelTitle :title="threadTitle" :isRead="isRead" @toggleRead="toggleRead" />
|
||||||
<Thread :source="selectedItem?.source ?? 0" :sourceItem="selectedItem?.sourceItem ?? 0" :thread="selectedItem?.thread ?? 0" :base-url="baseUrl" />
|
<Thread :source="selectedItem?.source ?? 0" :sourceItem="selectedItem?.sourceItem ?? 0" :thread="selectedItem?.thread ?? 0" :base-url="baseUrl" :highlighted="highlightedEvent" />
|
||||||
</div>
|
</div>
|
||||||
<Modal :model-value="showThread" class="block xl:hidden" @update:model-value="close">
|
<Modal :model-value="showThread" class="block xl:hidden" @update:model-value="close">
|
||||||
<template v-slot:header><div class="text-3xl">{{ threadTitle }}</div></template>
|
<template v-slot:header><div class="text-3xl">{{ threadTitle }}</div></template>
|
||||||
<template v-slot:body><Thread :source="selectedItem?.source ?? 0" :sourceItem="selectedItem?.sourceItem ?? 0" :thread="selectedItem?.thread ?? 0" :base-url="baseUrl" /></template>
|
<template v-slot:body>
|
||||||
|
<Thread :source="selectedItem?.source ?? 0" :sourceItem="selectedItem?.sourceItem ?? 0" :thread="selectedItem?.thread ?? 0" :base-url="baseUrl" :highlighted="highlightedEvent" />
|
||||||
|
</template>
|
||||||
</Modal>
|
</Modal>
|
||||||
<SnoozeModal :snoozing="snoozing" :threadRef="snoozingItem" @close="stopSnoozing" />
|
<SnoozeModal :snoozing="snoozing" :threadRef="snoozingItem" @close="stopSnoozing" />
|
||||||
</div>
|
</div>
|
||||||
|
@ -37,6 +39,11 @@ const route = useRoute();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|
||||||
const baseUrl = computed(() => `/source/${route.params.source}`);
|
const baseUrl = computed(() => `/source/${route.params.source}`);
|
||||||
|
const highlightedEvent = computed(() => {
|
||||||
|
let event = route.params.event;
|
||||||
|
event = Array.isArray(event) ? event[0] : event;
|
||||||
|
return event ? parseInt(event) : undefined;
|
||||||
|
});
|
||||||
|
|
||||||
const sourceId = computed(() => parseInt(deArray(route.params.source)));
|
const sourceId = computed(() => parseInt(deArray(route.params.source)));
|
||||||
const source = computed(() => sources.value[sourceId.value]);
|
const source = computed(() => sources.value[sourceId.value]);
|
||||||
|
|
|
@ -14,7 +14,7 @@ const routes: RouteRecordRaw[] = [
|
||||||
{ path: '/todo/:source(\\d+)?/:sourceItem(\\d+)?/:thread(\\d+)?', component: Todo, name: 'todo' },
|
{ path: '/todo/:source(\\d+)?/:sourceItem(\\d+)?/:thread(\\d+)?', component: Todo, name: 'todo' },
|
||||||
{ path: '/snoozed/:source(\\d+)?/:sourceItem(\\d+)?/:thread(\\d+)?', component: Snoozed, name: 'snoozed' },
|
{ path: '/snoozed/:source(\\d+)?/:sourceItem(\\d+)?/:thread(\\d+)?', component: Snoozed, name: 'snoozed' },
|
||||||
{ path: '/low/:source(\\d+)?/:sourceItem(\\d+)?/:thread(\\d+)?', component: LowPriority, name: 'low' },
|
{ path: '/low/:source(\\d+)?/:sourceItem(\\d+)?/:thread(\\d+)?', component: LowPriority, name: 'low' },
|
||||||
{ path: '/source/:source(\\d+)/:sourceItem(\\d+)?/:thread(\\d+)?', component: Source, name: 'source' },
|
{ path: '/source/:source(\\d+)/:sourceItem(\\d+)?/:thread(\\d+)?/:event(\\d+)?', component: Source, name: 'source' },
|
||||||
{ path: '/unsorted/:source(\\d+)?/:sourceItem(\\d+)?/:thread(\\d+)?', component: Unsorted, name: 'unsorted' },
|
{ path: '/unsorted/:source(\\d+)?/:sourceItem(\\d+)?/:thread(\\d+)?', component: Unsorted, name: 'unsorted' },
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
|
@ -193,7 +193,7 @@ export function parseString(message: string) {
|
||||||
if (referencedMessage.type === "message") {
|
if (referencedMessage.type === "message") {
|
||||||
parts.push(<span class="px-2 bg-emerald-200/25 rounded-2xl">
|
parts.push(<span class="px-2 bg-emerald-200/25 rounded-2xl">
|
||||||
{ referencedMessage.message }
|
{ referencedMessage.message }
|
||||||
<RouterLink class="text-slate-600 pl-2 text-sm" to={`/source/${sourceId}/${sourceItemId}/${threadId}`}>
|
<RouterLink class="text-slate-600 pl-2 text-sm" to={`/source/${sourceId}/${sourceItemId}/${threadId}/${messageId}`}>
|
||||||
goto message
|
goto message
|
||||||
</RouterLink>
|
</RouterLink>
|
||||||
</span>);
|
</span>);
|
||||||
|
|
Loading…
Reference in a new issue