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

Freegle / iznik-nuxt3 / 7e67c775-d33f-4962-83ce-21399370ae10

30 Jul 2025 08:22PM UTC coverage: 38.679% (+0.1%) from 38.533%
7e67c775-d33f-4962-83ce-21399370ae10

push

circleci

chriscant
Fix TN message handling. Bump to 3.2.1

1737 of 5814 branches covered (29.88%)

Branch coverage included in aggregate %.

0 of 1 new or added line in 1 file covered. (0.0%)

7 existing lines in 1 file now uncovered.

4305 of 9807 relevant lines covered (43.9%)

129.05 hits per line

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

1.47
/components/ChatMessageText.vue
1
<template>
2
  <!-- DO NOT COPY INTO MASTER -->
3
  <div
4
    class="chatMessageWrapper"
5
    :class="{ myChatMessage: messageIsFromCurrentUser }"
6
  >
7
    <div class="chatMessage forcebreak chatMessage__owner">
8
      <div>
9
        <span v-if="isMT && emessageMThasTNlinks">
10
          <!-- eslint-disable-next-line-->
11
          <span v-html="emessageMTTN" class="preline forcebreak"></span>
12
        </span>
13
        <span v-else-if="!highlightEmails">
14
          <span v-if="messageIsNew" class="prewrap font-weight-bold">{{
15
            emessage
16
          }}</span>
17
          <span v-else class="preline forcebreak">{{ emessage }}</span>
18
          <b-img
19
            v-if="chatmessage?.image"
20
            fluid
21
            :src="chatmessage?.image.path"
22
            lazy
23
            rounded
24
          />
25
        </span>
26
        <span v-else>
27
          <span v-if="messageIsNew" class="font-weight-bold">
28
            <Highlighter
29
              :text-to-highlight="emessage"
30
              :search-words="[regexEmailMT]"
31
              highlight-class-name="highlight"
32
              class="prewrap"
33
            />
34
          </span>
35
          <span v-else>
36
            <Highlighter
37
              :text-to-highlight="emessage"
38
              :search-words="[regexEmailMT]"
39
              highlight-class-name="highlight"
40
              class="preline forcebreak"
41
            />
42
          </span>
43
          <b-img
44
            v-if="chatmessage?.image"
45
            fluid
46
            :src="chatmessage?.image.path"
47
            lazy
48
            rounded
49
          />
50
        </span>
51
      </div>
52
      <div v-if="lat || lng">
53
        <l-map
54
          ref="map"
55
          :zoom="16"
56
          :max-zoom="maxZoom"
57
          :center="[lat, lng]"
58
          :style="'width: 100%; height: 200px'"
59
        >
60
          <l-tile-layer :url="osmtile()" :attribution="attribution()" />
61
          <l-marker :lat-lng="[lat, lng]" :interactive="false" />
62
        </l-map>
63
        <div class="small text-muted">
64
          (Map shows approximate location of {{ postcode }})
65
        </div>
66
      </div>
67
    </div>
68
    <div class="chatMessageProfilePic">
69
      <ProfileImage
70
        :image="chatMessageProfileImage"
71
        class="ml-1 mb-1 mt-1 inline"
72
        is-thumbnail
73
        size="sm"
74
      />
75
    </div>
76
  </div>
77
</template>
78
<script setup>
79
import Highlighter from 'vue-highlight-words'
80
import { useChatMessageBase } from '../composables/useChat'
81
import { ref, computed, onMounted } from '#imports'
82
import ProfileImage from '~/components/ProfileImage'
83
import { MAX_MAP_ZOOM, POSTCODE_REGEX } from '~/constants'
84
import { attribution, osmtile } from '~/composables/useMap'
85
import { useLocationStore } from '~/stores/location'
86
import { useMiscStore } from '~/stores/misc' // MT
87

88
const props = defineProps({
3✔
89
  chatid: {
×
90
    type: Number,
91
    required: true,
92
  },
93
  id: {
94
    type: Number,
95
    required: true,
96
  },
97
  pov: {
98
    type: Number,
99
    required: false,
100
    default: null,
101
  },
102
  highlightEmails: {
103
    type: Boolean,
104
    required: false,
105
    default: false,
106
  },
107
})
108

109
// Use properties from ChatBase component via composable
110
const {
111
  chat,
112
  chatmessage,
113
  emessage,
114
  messageIsFromCurrentUser,
115
  chatMessageProfileImage,
116
  regexEmailMT, // MT
117
} = useChatMessageBase(props.chatid, props.id, props.pov)
118

119
// Data properties
120
const lat = ref(null)
×
121
const lng = ref(null)
×
122
const miscStore = useMiscStore() // MT
×
123
const isMT = ref(miscStore.modtools) // MT
×
124

×
125
// Computed properties
126
const emessageMThasTNlinks = computed(() => {
127
  return emessage.value.includes('https://trashnothing.com/fd/')
×
128
})
129
const emessageMTTN = computed(() => {
×
NEW
130
  let ret = emessage.value
×
131
  ret = ret
×
132
    .replace(/&/g, '&amp;')
133
    .replace(/</g, '&lt;')
134
    .replace(/>/g, '&gt;')
135
    .replace(/"/g, '&quot;')
136
    .replace(/'/g, '&#39;')
137
  let tnpos = -1
×
138
  while (true) {
×
139
    tnpos = ret.indexOf('https://trashnothing.com/fd/', tnpos + 1)
×
140
    if (tnpos === -1) break
×
141
    const endtn = ret.indexOf('\n', tnpos)
×
142
    if (endtn === -1) break
×
143
    const tnurl = ret.substring(tnpos, endtn)
×
144
    const tnlink = '<a href=' + tnurl + ' target="_blank">' + tnurl + '</a>'
×
145
    ret = ret.substring(0, tnpos) + tnlink + ret.substring(endtn)
×
146
    tnpos += tnlink.length
×
147
  }
148
  return ret
×
149
})
150

151
const maxZoom = computed(() => MAX_MAP_ZOOM)
×
152

153
const messageIsNew = computed(() => {
×
154
  return (
×
155
    chatmessage.value?.secondsago < 60 ||
×
156
    chatmessage.value?.id > chat.value?.lastmsgseen
×
157
  )
158
})
159

160
const postcode = computed(() => {
×
161
  let ret = null
×
162

163
  const postcodeMatch = chatmessage.value?.message
×
164
    ?.toString()
165
    .match(POSTCODE_REGEX)
166

167
  if (postcodeMatch?.length) {
×
168
    if (!postcodeMatch[0].includes(' ')) {
×
169
      // Make sure we have a space in the right place, because this helps with autocomplete
170
      ret = postcodeMatch[0].replace(/^(.*)(\d)/, '$1 $2')
×
171
    } else {
172
      ret = postcodeMatch[0]
×
173
    }
174
  }
175

176
  return ret
×
177
})
178

179
// Lifecycle hooks
180
onMounted(async () => {
×
181
  if (postcode.value) {
×
182
    // Use typeahead to find the postcode location.
183
    const locationStore = useLocationStore()
×
184
    const locs = await locationStore.typeahead(postcode.value)
×
185

186
    if (locs?.length) {
×
187
      lat.value = locs[0].lat
×
188
      lng.value = locs[0].lng
×
189
    }
190
  }
191
})
192
</script>
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