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

Freegle / iznik-nuxt3 / 03b7ce59-0b72-4c64-ad83-a68e43b55ee7

09 Feb 2026 06:31PM UTC coverage: 43.797% (-0.3%) from 44.143%
03b7ce59-0b72-4c64-ad83-a68e43b55ee7

Pull #165

circleci

edwh
feat: Switch NoticeboardAPI write methods to v2

Switch add(), save(), and action() from $post/$patch to $postv2/$patchv2.
Also removes console.log from add().

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Pull Request #165: feat: Switch NoticeboardAPI writes to v2

3745 of 8748 branches covered (42.81%)

Branch coverage included in aggregate %.

1706 of 3698 relevant lines covered (46.13%)

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

131
const miscStore = useMiscStore()
×
132

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

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

166
// Data properties
167
const lat = ref(null)
168
const lng = ref(null)
169

170
// Computed properties
171
const maxZoom = computed(() => MAX_MAP_ZOOM)
172

173
const messageIsNew = computed(() => {
174
  return (
×
175
    chatmessage.value?.secondsago < 60 ||
×
176
    chatmessage.value?.id > chat.value?.lastmsgseen
×
177
  )
178
})
179

180
const postcode = computed(() => {
181
  let ret = null
×
182

183
  const postcodeMatch = chatmessage.value?.message.match(POSTCODE_REGEX)
×
184

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

194
  return ret
195
})
196

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

200
// Linkified message for ModTools (without email highlighting)
201
const linkifiedMessage = computed(() => {
202
  return linkifyText(emessage.value)
203
})
204

205
// Linkified message with email highlighting for ModTools chat review
206
const linkifiedAndHighlightedMessage = computed(() => {
207
  return linkifyAndHighlightEmails(emessage.value, regexEmail.value)
208
})
209

210
// Lifecycle hooks
211
onMounted(async () => {
212
  if (postcode.value) {
×
213
    // Use typeahead to find the postcode location.
214
    const locationStore = useLocationStore()
215
    const locs = await locationStore.typeahead(postcode.value)
×
216

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

230
  &:hover {
231
    text-decoration: none;
232
  }
233
}
234

235
/* Email highlight styling for ModTools - matches the Highlighter component */
236
:deep(.highlight) {
237
  color: $color-blue--base;
238
  background-color: initial;
239
}
240

241
.chat-empty-message {
242
  font-style: italic;
243
  color: $color-gray--dark;
244
}
245
</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