• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

Freegle / iznik-nuxt3 / 8e743ced-42af-4ccb-8b4a-3ceb0f41c7cf

06 Feb 2026 09:42AM UTC coverage: 44.677% (-0.2%) from 44.827%
8e743ced-42af-4ccb-8b4a-3ceb0f41c7cf

push

circleci

CircleCI Auto-merge
Auto-merge master to production after successful tests - Original commit: fix: ModTools chat badge not updating - add error handling and fix search blocking

3676 of 8388 branches covered (43.82%)

Branch coverage included in aggregate %.

1670 of 3578 relevant lines covered (46.67%)

79.05 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/components/ChatMessageText.vue
1
<template>
2
  <div
3
    class="chatMessageWrapper"
4
    :class="{ myChatMessage: messageIsFromCurrentUser }"
5
  >
6
    <div class="chatMessageProfilePic">
7
      <ProfileImage
8
        :image="chatMessageProfileImage"
9
        :name="chatMessageProfileName"
10
        class="inline"
11
        is-thumbnail
12
        size="sm"
13
      />
14
    </div>
15
    <div class="chatMessage forcebreak chatMessage__owner">
16
      <div>
17
        <!-- ModTools: clickable links enabled -->
18
        <template v-if="isModTools">
×
19
          <span v-if="!highlightEmails">
20
            <span
21
              v-if="messageIsNew"
×
22
              class="prewrap font-weight-bold"
23
              v-html="linkifiedMessage"
24
            />
25
            <span v-else class="preline forcebreak" v-html="linkifiedMessage" />
×
26
            <b-img
27
              v-if="chatmessage?.image"
28
              fluid
29
              :src="chatmessage?.image.path"
×
30
              lazy
31
              rounded
32
            />
33
          </span>
34
          <span v-else>
35
            <span
36
              v-if="messageIsNew"
×
37
              class="prewrap font-weight-bold"
38
              v-html="linkifiedAndHighlightedMessage"
39
            />
40
            <span
41
              v-else
42
              class="preline forcebreak"
43
              v-html="linkifiedAndHighlightedMessage"
×
44
            />
45
            <b-img
46
              v-if="chatmessage?.image"
47
              fluid
48
              :src="chatmessage?.image.path"
×
49
              lazy
50
              rounded
51
            />
52
          </span>
53
        </template>
54
        <!-- Freegle: no clickable links for safety -->
55
        <template v-else>
56
          <span v-if="!highlightEmails">
57
            <span v-if="messageIsNew" class="prewrap font-weight-bold">{{
×
58
              emessage
59
            }}</span>
60
            <span v-else class="preline forcebreak">{{ emessage }}</span>
×
61
            <b-img
62
              v-if="chatmessage?.image"
63
              fluid
64
              :src="chatmessage?.image.path"
×
65
              lazy
66
              rounded
67
            />
68
          </span>
69
          <span v-else>
70
            <span v-if="messageIsNew" class="font-weight-bold">
×
71
              <Highlighter
72
                :text-to-highlight="emessage"
73
                :search-words="[regexEmail]"
74
                highlight-class-name="highlight"
75
                class="prewrap"
76
              />
77
            </span>
78
            <span v-else>
79
              <Highlighter
80
                :text-to-highlight="emessage"
81
                :search-words="[regexEmail]"
82
                highlight-class-name="highlight"
83
                class="preline forcebreak"
×
84
              />
85
            </span>
86
            <b-img
87
              v-if="chatmessage?.image"
88
              fluid
89
              :src="chatmessage?.image.path"
×
90
              lazy
91
              rounded
92
            />
93
          </span>
94
        </template>
95
      </div>
96
      <div v-if="lat || lng">
×
97
        <l-map
98
          ref="map"
99
          :zoom="16"
100
          :max-zoom="maxZoom"
101
          :center="[lat, lng]"
102
          :style="'width: 100%; height: 200px'"
103
        >
104
          <l-tile-layer :url="osmtile()" :attribution="attribution()" />
105
          <l-marker :lat-lng="[lat, lng]" :interactive="false" />
106
        </l-map>
107
        <div class="small text-muted">
108
          (Map shows approximate location of {{ postcode }})
109
        </div>
110
      </div>
111
    </div>
112
  </div>
113
</template>
114
<script setup>
115
import Highlighter from 'vue-highlight-words'
116
import { useChatMessageBase } from '~/composables/useChat'
117
import {
118
  linkifyText,
119
  linkifyAndHighlightEmails,
120
} from '~/composables/useLinkify'
121
import { ref, computed, onMounted } from '#imports'
122
import ProfileImage from '~/components/ProfileImage'
123
import { MAX_MAP_ZOOM, POSTCODE_REGEX } from '~/constants'
124
import { attribution, osmtile } from '~/composables/useMap'
125
import { useLocationStore } from '~/stores/location'
126
import { useMiscStore } from '~/stores/misc'
127

128
const miscStore = useMiscStore()
×
129

130
const props = defineProps({
131
  chatid: {
132
    type: Number,
133
    required: true,
134
  },
135
  id: {
136
    type: Number,
137
    required: true,
138
  },
139
  pov: {
140
    type: Number,
141
    required: false,
142
    default: null,
143
  },
144
  highlightEmails: {
145
    type: Boolean,
146
    required: false,
147
    default: false,
148
  },
149
})
150

151
// Use properties from ChatBase component via composable
152
const {
153
  chat,
154
  chatmessage,
155
  emessage,
156
  messageIsFromCurrentUser,
157
  chatMessageProfileImage,
158
  chatMessageProfileName,
159
  regexEmail,
160
} = useChatMessageBase(props.chatid, props.id, props.pov)
161

162
// Data properties
163
const lat = ref(null)
164
const lng = ref(null)
165

166
// Computed properties
167
const maxZoom = computed(() => MAX_MAP_ZOOM)
168

169
const messageIsNew = computed(() => {
170
  return (
×
171
    chatmessage.value?.secondsago < 60 ||
×
172
    chatmessage.value?.id > chat.value?.lastmsgseen
×
173
  )
174
})
175

176
const postcode = computed(() => {
177
  let ret = null
×
178

179
  const postcodeMatch = chatmessage.value?.message.match(POSTCODE_REGEX)
×
180

181
  if (postcodeMatch?.length) {
×
182
    if (!postcodeMatch[0].includes(' ')) {
×
183
      // Make sure we have a space in the right place, because this helps with autocomplete
184
      ret = postcodeMatch[0].replace(/^(.*)(\d)/, '$1 $2')
185
    } else {
186
      ret = postcodeMatch[0]
187
    }
188
  }
189

190
  return ret
191
})
192

193
// In ModTools, we make URLs clickable. In Freegle, we don't for safety reasons.
194
const isModTools = computed(() => miscStore.modtools)
195

196
// Linkified message for ModTools (without email highlighting)
197
const linkifiedMessage = computed(() => {
198
  return linkifyText(emessage.value)
199
})
200

201
// Linkified message with email highlighting for ModTools chat review
202
const linkifiedAndHighlightedMessage = computed(() => {
203
  return linkifyAndHighlightEmails(emessage.value, regexEmail.value)
204
})
205

206
// Lifecycle hooks
207
onMounted(async () => {
208
  if (postcode.value) {
×
209
    // Use typeahead to find the postcode location.
210
    const locationStore = useLocationStore()
211
    const locs = await locationStore.typeahead(postcode.value)
×
212

213
    if (locs?.length) {
×
214
      lat.value = locs[0].lat
215
      lng.value = locs[0].lng
216
    }
217
  }
218
})
219
</script>
220
<style scoped lang="scss">
221
/* Chat link styling for ModTools - uses :deep() since content is rendered via v-html */
222
:deep(.chat-link) {
223
  color: $color-blue--base;
224
  text-decoration: underline;
225

226
  &:hover {
227
    text-decoration: none;
228
  }
229
}
230

231
/* Email highlight styling for ModTools - matches the Highlighter component */
232
:deep(.highlight) {
233
  color: $color-blue--base;
234
  background-color: initial;
235
}
236
</style>
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc