Skip to content

Commit 743da66

Browse files
AchoArnoldCopilot
andcommitted
feat(web): display contact names on threads
Preserve contact details across mark-read updates. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 4aff603 commit 743da66

5 files changed

Lines changed: 52 additions & 9 deletions

File tree

‎web/app/components/MessageThread.vue‎

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,13 @@ import {
77
mdiAlert,
88
mdiAccount,
99
} from '@mdi/js'
10-
import { formatPhoneNumber, startsWithLetter } from '~/utils/filters'
10+
import type { EntitiesMessageThread } from '~~/shared/types/api'
1111
1212
const threadsStore = useThreadsStore()
1313
const phonesStore = usePhonesStore()
1414
const appStore = useAppStore()
1515
const notificationsStore = useNotificationsStore()
16+
const { formatPhoneNumber, startsWithLetter } = useFilters()
1617
1718
function threadDate(date: string): string {
1819
return new Date(date).toLocaleString(undefined, {
@@ -27,6 +28,21 @@ function onInstallApp() {
2728
message: 'Downloading the httpSMS Android App',
2829
})
2930
}
31+
32+
function threadContactName(thread: EntitiesMessageThread): string {
33+
return thread.contact_details?.name?.trim() ?? ''
34+
}
35+
36+
function threadContactTitle(thread: EntitiesMessageThread): string {
37+
return threadContactName(thread) || formatPhoneNumber(thread.contact)
38+
}
39+
40+
function threadAvatarInitial(thread: EntitiesMessageThread): string {
41+
const contactName = threadContactName(thread)
42+
if (contactName) return contactName.substring(0, 1)
43+
44+
return startsWithLetter(thread.contact) ? thread.contact.substring(0, 1) : ''
45+
}
3046
</script>
3147

3248
<template>
@@ -104,16 +120,16 @@ function onInstallApp() {
104120
size="40"
105121
:badge="thread.is_read ? false : { color: 'primary', dotSize: 12 }"
106122
>
107-
<v-icon v-if="!startsWithLetter(thread.contact)" color="white">{{
123+
<v-icon v-if="!threadAvatarInitial(thread)" color="white">{{
108124
mdiAccount
109125
}}</v-icon>
110126
<span v-else class="text-white text-headline-small">{{
111-
thread.contact.substring(0, 1)
127+
threadAvatarInitial(thread)
112128
}}</span>
113129
</v-avatar>
114130
</template>
115131
<v-list-item-title :class="{ 'font-weight-bold': !thread.is_read }">{{
116-
formatPhoneNumber(thread.contact)
132+
threadContactTitle(thread)
117133
}}</v-list-item-title>
118134
<v-list-item-subtitle
119135
class="text-truncate mt-1"

‎web/app/components/MessageThreadHeader.vue‎

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,9 @@ import {
1414
mdiDotsVertical,
1515
mdiMagnify,
1616
mdiCommentTextMultipleOutline,
17+
mdiAccountMultiple,
1718
mdiCircle,
1819
} from '@mdi/js'
19-
import { formatPhoneNumber, phoneCountry, humanizeTime } from '~/utils/filters'
2020
import type { EntitiesPhone } from '~~/shared/types/api'
2121
2222
const router = useRouter()
@@ -28,6 +28,7 @@ const threadsStore = useThreadsStore()
2828
const appStore = useAppStore()
2929
const notificationsStore = useNotificationsStore()
3030
const redirectPreferenceStore = useRedirectPreferenceStore()
31+
const { formatPhoneNumber, phoneCountry, humanizeTime } = useFilters()
3132
3233
const selectedMenuItem = ref(-1)
3334
@@ -180,6 +181,10 @@ async function logout() {
180181
<template #prepend><v-icon :icon="mdiMagnify" /></template>
181182
<v-list-item-title>Search Messages</v-list-item-title>
182183
</v-list-item>
184+
<v-list-item :to="{ name: 'contacts' }">
185+
<template #prepend><v-icon :icon="mdiAccountMultiple" /></template>
186+
<v-list-item-title>Contacts</v-list-item-title>
187+
</v-list-item>
183188
<v-list-item :to="{ name: 'settings' }">
184189
<template #prepend><v-icon :icon="mdiAccountCog" /></template>
185190
<v-list-item-title>Settings</v-list-item-title>

‎web/app/composables/useFilters.ts‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import {
77
formatBillingPeriod,
88
formatBillingPeriodDateOrdinal,
99
humanizeTime,
10+
startsWithLetter,
1011
} from '../utils/filters'
1112
import { capitalize } from '../utils/capitalize'
1213

@@ -20,6 +21,7 @@ export function useFilters() {
2021
formatBillingPeriod,
2122
formatBillingPeriodDateOrdinal,
2223
humanizeTime,
24+
startsWithLetter,
2325
capitalize,
2426
}
2527
}

‎web/app/pages/threads/[id]/index.vue‎

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ import Pusher from 'pusher-js'
1919
import type { Channel } from 'pusher-js'
2020
import { isValidPhoneNumber } from 'libphonenumber-js'
2121
import type { EntitiesMessage } from '~~/shared/types/api'
22-
import { startsWithLetter } from '~/utils/filters'
2322
2423
definePageMeta({
2524
middleware: ['auth'],
@@ -33,7 +32,7 @@ const route = useRoute()
3332
const router = useRouter()
3433
const config = useRuntimeConfig()
3534
const { lgAndUp, mdAndDown, mdAndUp } = useDisplay()
36-
const { formatPhoneNumber } = useFilters()
35+
const { formatPhoneNumber, startsWithLetter } = useFilters()
3736
const notificationsStore = useNotificationsStore()
3837
const authStore = useAuthStore()
3938
const phonesStore = usePhonesStore()
@@ -104,6 +103,14 @@ function formatAttachmentName(url: string): string {
104103
return url
105104
}
106105
106+
function currentThreadContactTitle(): string {
107+
const thread = threadsStore.currentThread
108+
if (!thread) return ''
109+
return (
110+
thread.contact_details?.name?.trim() || formatPhoneNumber(thread.contact)
111+
)
112+
}
113+
107114
function scrollToElement() {
108115
const el = messageBody.value
109116
if (el) {
@@ -268,7 +275,7 @@ onBeforeUnmount(() => {
268275
<VIcon :icon="mdiArrowLeft" />
269276
</VBtn>
270277
<VToolbarTitle v-if="threadsStore.currentThread">
271-
{{ formatPhoneNumber(threadsStore.currentThread.contact) }}
278+
{{ currentThreadContactTitle() }}
272279
</VToolbarTitle>
273280
<VMenu>
274281
<template #activator="{ props }">

‎web/app/stores/threads.ts‎

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,19 @@ export const useThreadsStore = defineStore('threads', () => {
2828
const index = threads.value.findIndex(
2929
(thread) => thread.id === updatedThread.id,
3030
)
31-
if (index !== -1) threads.value[index] = updatedThread
31+
if (index !== -1) {
32+
const existingThread = threads.value[index]
33+
threads.value[index] = {
34+
...updatedThread,
35+
// Mark-read PUT responses are not contact-enriched; keep the resolved
36+
// contact so display names do not disappear after the thread updates.
37+
contact_details:
38+
updatedThread.contact_details ??
39+
(updatedThread.contact === existingThread.contact
40+
? existingThread.contact_details
41+
: undefined),
42+
}
43+
}
3244
}
3345

3446
async function loadThreads() {
@@ -45,6 +57,7 @@ export const useThreadsStore = defineStore('threads', () => {
4557
owner: phonesStore.owner ?? phonesStore.phones[0]?.phone_number,
4658
limit: 100,
4759
is_archived: archivedThreads.value,
60+
contacts: true,
4861
},
4962
},
5063
)

0 commit comments

Comments
 (0)