Merge branch 'catch-component-errors' into 'dev'

Handle message rendering errors via the errorCaptured hook

See merge request keanuapp/keanuapp-weblite!298
This commit is contained in:
N Pex 2024-06-07 13:06:05 +00:00
commit 134b1f8cff
5 changed files with 68 additions and 34 deletions

View file

@ -109,7 +109,8 @@
"files": "Files", "files": "Files",
"images": "Images", "images": "Images",
"send_attachements_dialog_title": "Do you want to send following attachments ?", "send_attachements_dialog_title": "Do you want to send following attachments ?",
"download_all": "Download all" "download_all": "Download all",
"failed_to_render": "Failed to render event"
}, },
"room": { "room": {
"invitations": "You have no invitations | You have 1 invitation | You have {count} invitations", "invitations": "You have no invitations | You have 1 invitation | You have {count} invitations",

View file

@ -62,35 +62,37 @@
<div v-if="showDayMarkerBeforeEvent(event) && !!componentForEvent(event, isForExport = false)" class="day-marker"><div class="line"></div><div class="text">{{ dayForEvent(event) }}</div><div class="line"></div></div> <div v-if="showDayMarkerBeforeEvent(event) && !!componentForEvent(event, isForExport = false)" class="day-marker"><div class="line"></div><div class="text">{{ dayForEvent(event) }}</div><div class="line"></div></div>
<div v-if="!event.isRelation() && !event.isRedaction()" :ref="event.getId()"> <div v-if="!event.isRelation() && !event.isRedaction()" :ref="event.getId()">
<div class="message-wrapper" v-on:touchstart=" <MessageErrorHandler>
(e) => { <div class="message-wrapper" v-on:touchstart="
touchStart(e, event); (e) => {
} touchStart(e, event);
" v-on:touchend="touchEnd" v-on:touchcancel="touchCancel" v-on:touchmove="touchMove"> }
<!-- Note: For threaded media messages, IF there is only one item we show that media item as a single component. " v-on:touchend="touchEnd" v-on:touchcancel="touchCancel" v-on:touchmove="touchMove">
We might therefore get calls to v-on:context-menu that has the event set to that single media item, not the top level thread event <!-- Note: For threaded media messages, IF there is only one item we show that media item as a single component.
that is really displayed in the flow. Therefore, we rewrite these events with "{event: event, anchor: $event.anchor}", We might therefore get calls to v-on:context-menu that has the event set to that single media item, not the top level thread event
see below. Otherwise things like context menus won't work as designed. that is really displayed in the flow. Therefore, we rewrite these events with "{event: event, anchor: $event.anchor}",
--> see below. Otherwise things like context menus won't work as designed.
<component :is="componentForEvent(event)" :room="room" :originalEvent="event" :nextEvent="filteredEvents[index + 1]" -->
:timelineSet="timelineSet" v-on:send-quick-reaction.stop="sendQuickReaction" <component :is="componentForEvent(event)" :room="room" :originalEvent="event" :nextEvent="filteredEvents[index + 1]"
:componentFn="componentForEvent" :timelineSet="timelineSet" v-on:send-quick-reaction.stop="sendQuickReaction"
v-on:context-menu="showContextMenuForEvent({event: event, anchor: $event.anchor})" :componentFn="componentForEvent"
v-on:own-avatar-clicked="viewProfile" v-on:context-menu="showContextMenuForEvent({event: event, anchor: $event.anchor})"
v-on:other-avatar-clicked="showAvatarMenuForEvent({event: event, anchor: $event.anchor})" v-on:own-avatar-clicked="viewProfile"
v-on:download="download(event)" v-on:other-avatar-clicked="showAvatarMenuForEvent({event: event, anchor: $event.anchor})"
v-on:poll-closed="pollWasClosed(event)" v-on:download="download(event)"
v-on:more=" v-on:poll-closed="pollWasClosed(event)"
isEmojiQuickReaction = true v-on:more="
showMoreMessageOperations({event: event, anchor: $event.anchor}) isEmojiQuickReaction = true
" showMoreMessageOperations({event: event, anchor: $event.anchor})
v-on:layout-change="onLayoutChange" "
v-on:addQuickHeartReaction="addQuickHeartReaction(event)" v-on:layout-change="onLayoutChange"
/> v-on:addQuickHeartReaction="addQuickHeartReaction(event)"
<!-- <div v-if="debugging" style="user-select:text">EventID: {{ event.getId() }}</div> --> />
<!-- <div v-if="debugging" style="user-select:text">Event: {{ JSON.stringify(event) }}</div> --> <!-- <div v-if="debugging" style="user-select:text">EventID: {{ event.getId() }}</div> -->
<div v-if="event.getId() == readMarker && index < filteredEvents.length - 1" class="read-marker"><div class="line"></div><div class="text">{{ $t('message.unread_messages') }}</div><div class="line"></div></div> <!-- <div v-if="debugging" style="user-select:text">Event: {{ JSON.stringify(event) }}</div> -->
</div> <div v-if="event.getId() == readMarker && index < filteredEvents.length - 1" class="read-marker"><div class="line"></div><div class="text">{{ $t('message.unread_messages') }}</div><div class="line"></div></div>
</div>
</MessageErrorHandler>
</div> </div>
</div> </div>
@ -213,7 +215,7 @@
</v-container> </v-container>
<input ref="attachment" type="file" name="attachment" @change="handlePickedAttachment($event)" <input ref="attachment" type="file" name="attachment" @change="handlePickedAttachment($event)"
accept="image/*,audio/*,video/*,.pdf,application/pdf,.apk,application/vnd.android.package-archive,.ipa,.zip,application/zip,application/x-zip-compressed,multipart/x-zip" class="d-none" multiple/> accept="image/*,audio/*,video/*,.mp3,.mp4,.wav,.m4a,.pdf,application/pdf,.apk,application/vnd.android.package-archive,.ipa,.zip,application/zip,application/x-zip-compressed,multipart/x-zip" class="d-none" multiple/>
<div v-if="currentFileInputsDialog && !useFileModeNonAdmin"> <div v-if="currentFileInputsDialog && !useFileModeNonAdmin">
<v-dialog v-model="currentFileInputsDialog" class="ma-0 pa-0" :width="$vuetify.breakpoint.smAndUp ? '50%' : '85%'" persistent scrollable> <v-dialog v-model="currentFileInputsDialog" class="ma-0 pa-0" :width="$vuetify.breakpoint.smAndUp ? '50%' : '85%'" persistent scrollable>
@ -364,6 +366,7 @@ import FileDropLayout from "./file_mode/FileDropLayout";
import roomTypeMixin from "./roomTypeMixin"; import roomTypeMixin from "./roomTypeMixin";
import roomMembersMixin from "./roomMembersMixin"; import roomMembersMixin from "./roomMembersMixin";
import PurgeRoomDialog from "../components/PurgeRoomDialog"; import PurgeRoomDialog from "../components/PurgeRoomDialog";
import MessageErrorHandler from "./MessageErrorHandler";
const sizeOf = require("image-size"); const sizeOf = require("image-size");
const dataUriToBuffer = require("data-uri-to-buffer"); const dataUriToBuffer = require("data-uri-to-buffer");
@ -417,7 +420,8 @@ export default {
FileDropLayout, FileDropLayout,
UserProfileDialog, UserProfileDialog,
PurgeRoomDialog, PurgeRoomDialog,
WelcomeHeaderChannelUser WelcomeHeaderChannelUser,
MessageErrorHandler
}, },
data() { data() {

View file

@ -0,0 +1,29 @@
<template>
<div>
<slot
v-if="err"
name="error"
v-bind:err="err"
><div class="text-center">{{ $t('message.failed_to_render') }}</div></slot>
<slot v-else></slot>
</div>
</template>
<script>
export default {
name: "MessageErrorHandler",
data() {
return {
err: false,
};
},
errorCaptured(err, ignoredvm, ignoredinfo) {
this.err = err;
return false;
}
};
</script>
<style lang="scss" scoped>
</style>

View file

@ -27,7 +27,7 @@ export default {
components: { MessageIncoming }, components: { MessageIncoming },
data() { data() {
return { return {
src: null, src: undefined,
cover: true, cover: true,
contain: false, contain: false,
dialog: false dialog: false

View file

@ -27,7 +27,7 @@ export default {
components: { MessageOutgoing }, components: { MessageOutgoing },
data() { data() {
return { return {
src: null, src: undefined,
cover: true, cover: true,
contain: false, contain: false,
dialog: false dialog: false